@firecms/core 3.0.0-beta.12 → 3.0.0-beta.14
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 +13 -11
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
- package/dist/components/EntityJsonPreview.d.ts +3 -0
- package/dist/components/EntityPreview.d.ts +4 -2
- package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
- package/dist/components/index.d.ts +1 -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/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
- package/dist/hooks/data/save.d.ts +1 -1
- package/dist/hooks/useBuildNavigationController.d.ts +2 -9
- package/dist/index.es.js +9251 -8721
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +16686 -16160
- 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 +26 -2
- package/dist/types/fields.d.ts +4 -1
- package/dist/types/firecms.d.ts +3 -2
- package/dist/types/navigation.d.ts +9 -0
- package/dist/types/plugins.d.ts +13 -0
- package/dist/types/properties.d.ts +22 -1
- package/dist/types/side_entity_controller.d.ts +4 -0
- package/dist/util/callbacks.d.ts +2 -0
- package/dist/util/createFormexStub.d.ts +2 -0
- package/dist/util/index.d.ts +1 -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 +9 -6
- package/src/app/Scaffold.tsx +13 -3
- package/src/components/ArrayContainer.tsx +414 -282
- package/src/components/ClearFilterSortButton.tsx +1 -1
- package/src/components/ConfirmationDialog.tsx +9 -9
- package/src/components/DeleteEntityDialog.tsx +4 -2
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +8 -6
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +58 -29
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -6
- package/src/components/EntityJsonPreview.tsx +66 -0
- package/src/components/EntityPreview.tsx +27 -16
- package/src/components/EntityView.tsx +4 -1
- package/src/components/ErrorView.tsx +1 -1
- package/src/components/HomePage/DefaultHomePage.tsx +2 -1
- package/src/components/HomePage/NavigationCardBinding.tsx +3 -1
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -1
- package/src/components/SelectableTable/SelectableTable.tsx +140 -143
- package/src/components/VirtualTable/VirtualTable.tsx +37 -31
- package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
- package/src/components/common/default_entity_actions.tsx +15 -4
- package/src/components/index.tsx +2 -0
- package/src/core/DefaultAppBar.tsx +17 -5
- package/src/core/EntityEditView.tsx +135 -47
- package/src/core/EntitySidePanel.tsx +15 -20
- package/src/core/FireCMS.tsx +19 -12
- package/src/form/EntityForm.tsx +32 -24
- package/src/form/PropertyFieldBinding.tsx +8 -6
- package/src/form/components/CustomIdField.tsx +3 -1
- package/src/form/components/LabelWithIcon.tsx +1 -1
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +4 -1
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -2
- package/src/form/field_bindings/BlockFieldBinding.tsx +1 -1
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
- package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +6 -2
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +3 -3
- package/src/form/field_bindings/RepeatFieldBinding.tsx +9 -2
- package/src/form/field_bindings/SelectFieldBinding.tsx +3 -2
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +227 -156
- package/src/hooks/data/save.ts +24 -35
- package/src/hooks/useBuildNavigationController.tsx +42 -17
- 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/components/ImagePreview.tsx +2 -2
- package/src/preview/components/ReferencePreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +5 -4
- 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 +1 -1
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
- package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
- package/src/routes/FireCMSRoute.tsx +15 -4
- package/src/types/collections.ts +31 -2
- package/src/types/fields.tsx +5 -1
- package/src/types/firecms.tsx +3 -3
- package/src/types/navigation.ts +11 -0
- package/src/types/plugins.tsx +17 -0
- package/src/types/properties.ts +27 -1
- package/src/types/side_entity_controller.tsx +5 -0
- package/src/util/callbacks.ts +119 -0
- package/src/util/createFormexStub.tsx +62 -0
- package/src/util/index.ts +1 -0
- package/src/util/join_collections.ts +3 -1
- package/src/util/navigation_from_path.ts +5 -1
- package/src/util/navigation_utils.ts +84 -20
- package/src/util/objects.ts +54 -17
- 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
- package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
- package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
|
@@ -32,29 +32,90 @@ export function getLastSegment(path: string) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export function resolveCollectionPathIds(path: string, allCollections: EntityCollection[]): string {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (subpaths.length % 2 === 0) {
|
|
39
|
-
throw Error(`resolveCollectionPathAliases: Collection paths must have an odd number of segments: ${path}`);
|
|
35
|
+
let remainingPath = removeInitialAndTrailingSlashes(path);
|
|
36
|
+
if (!remainingPath) {
|
|
37
|
+
return "";
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (aliasedCollection) {
|
|
45
|
-
resolvedAliased = aliasedCollection.path;
|
|
46
|
-
}
|
|
40
|
+
let currentCollections: EntityCollection[] | undefined = allCollections;
|
|
41
|
+
const resolvedPathParts: string[] = [];
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
while (remainingPath.length > 0) {
|
|
44
|
+
if (!currentCollections || currentCollections.length === 0) {
|
|
45
|
+
// We have remaining path segments but no more collections to match against
|
|
46
|
+
console.warn(`resolveCollectionPathIds: Path structure implies subcollections, but none found before segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
|
|
47
|
+
resolvedPathParts.push(remainingPath);
|
|
48
|
+
remainingPath = ""; // Stop processing
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let foundMatch = false;
|
|
53
|
+
// Sort potential matches by length descending to prioritize longer matches (e.g., "a/b" over "a")
|
|
54
|
+
const potentialMatches: { col: EntityCollection; match: string; }[] = currentCollections
|
|
55
|
+
.flatMap(col => [{
|
|
56
|
+
col,
|
|
57
|
+
match: col.path
|
|
58
|
+
}, {
|
|
59
|
+
col,
|
|
60
|
+
match: col.id
|
|
61
|
+
}])
|
|
62
|
+
.filter(p => p.match && remainingPath.startsWith(p.match))
|
|
63
|
+
.sort((a, b) => b.match.length - a.match.length);
|
|
64
|
+
|
|
65
|
+
if (potentialMatches.length > 0) {
|
|
66
|
+
const {
|
|
67
|
+
col: foundCollection,
|
|
68
|
+
match: matchString
|
|
69
|
+
} = potentialMatches[0];
|
|
70
|
+
|
|
71
|
+
resolvedPathParts.push(foundCollection.path); // Use the defined path
|
|
72
|
+
remainingPath = removeInitialSlash(remainingPath.substring(matchString.length));
|
|
73
|
+
|
|
74
|
+
// Check if we are at the end of the path
|
|
75
|
+
if (remainingPath.length === 0) {
|
|
76
|
+
foundMatch = true;
|
|
77
|
+
break; // Path ends with a collection segment
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// The next segment must be an entity ID
|
|
81
|
+
const idSeparatorIndex = remainingPath.indexOf("/");
|
|
82
|
+
let entityId: string;
|
|
83
|
+
if (idSeparatorIndex > -1) {
|
|
84
|
+
entityId = remainingPath.substring(0, idSeparatorIndex);
|
|
85
|
+
remainingPath = remainingPath.substring(idSeparatorIndex + 1);
|
|
86
|
+
} else {
|
|
87
|
+
// This should not happen if the original path is valid (odd segments)
|
|
88
|
+
// but handle it defensively: assume the rest is the ID
|
|
89
|
+
entityId = remainingPath;
|
|
90
|
+
remainingPath = "";
|
|
91
|
+
console.warn(`resolveCollectionPathIds: Path seems to end with an entity ID "${entityId}" instead of a collection segment in original path "${path}". This might indicate an invalid input path.`);
|
|
92
|
+
// Even if it ends here, we still need to push the ID
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
resolvedPathParts.push(entityId); // Append entity ID
|
|
96
|
+
currentCollections = foundCollection.subcollections; // Move to subcollections
|
|
97
|
+
foundMatch = true;
|
|
98
|
+
|
|
99
|
+
if (!currentCollections && remainingPath.length > 0) {
|
|
100
|
+
// Warn if the path continues but no subcollections were defined
|
|
101
|
+
console.warn(`resolveCollectionPathIds: Path continues after entity ID "${entityId}", but no subcollections are defined for the preceding collection "${foundCollection.path}" in path "${path}". Appending remaining original path.`);
|
|
102
|
+
resolvedPathParts.push(remainingPath); // Append the rest
|
|
103
|
+
remainingPath = ""; // Stop processing
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!foundMatch) {
|
|
110
|
+
// Collection definition not found for the start of the remaining path
|
|
111
|
+
console.warn(`resolveCollectionPathIds: Collection definition not found for segment starting with "${remainingPath}" in original path "${path}". Appending remaining original path.`);
|
|
112
|
+
resolvedPathParts.push(remainingPath); // Append the rest
|
|
113
|
+
remainingPath = ""; // Stop processing
|
|
114
|
+
break;
|
|
52
115
|
}
|
|
53
|
-
const restOfThePath = cleanPath.split("/").slice(2).join("/");
|
|
54
|
-
return (resolvedAliased ?? subpaths[0]) + "/" + subpaths[1] + "/" + resolveCollectionPathIds(restOfThePath, segmentCollection.subcollections);
|
|
55
|
-
} else {
|
|
56
|
-
return resolvedAliased ?? cleanPath;
|
|
57
116
|
}
|
|
117
|
+
|
|
118
|
+
return resolvedPathParts.join("/");
|
|
58
119
|
}
|
|
59
120
|
|
|
60
121
|
/**
|
|
@@ -116,6 +177,7 @@ export function navigateToEntity({
|
|
|
116
177
|
entityId,
|
|
117
178
|
copy,
|
|
118
179
|
path,
|
|
180
|
+
fullIdPath,
|
|
119
181
|
selectedTab,
|
|
120
182
|
sideEntityController,
|
|
121
183
|
onClose,
|
|
@@ -129,6 +191,7 @@ export function navigateToEntity({
|
|
|
129
191
|
selectedTab?: string;
|
|
130
192
|
copy?: boolean;
|
|
131
193
|
path: string;
|
|
194
|
+
fullIdPath?: string;
|
|
132
195
|
sideEntityController: SideEntityController;
|
|
133
196
|
onClose?: () => void;
|
|
134
197
|
navigation: NavigationController
|
|
@@ -138,7 +201,8 @@ export function navigateToEntity({
|
|
|
138
201
|
|
|
139
202
|
sideEntityController.open({
|
|
140
203
|
entityId,
|
|
141
|
-
path,
|
|
204
|
+
path: fullIdPath ?? path,
|
|
205
|
+
// fullIdPath,
|
|
142
206
|
copy,
|
|
143
207
|
selectedTab,
|
|
144
208
|
collection,
|
|
@@ -147,7 +211,7 @@ export function navigateToEntity({
|
|
|
147
211
|
});
|
|
148
212
|
|
|
149
213
|
} else {
|
|
150
|
-
let to = navigation.buildUrlCollectionPath(entityId ? `${path}/${entityId}` : path);
|
|
214
|
+
let to = navigation.buildUrlCollectionPath(entityId ? `${fullIdPath ?? path}/${entityId}` : fullIdPath ?? path);
|
|
151
215
|
if (entityId && selectedTab) {
|
|
152
216
|
to += `/${selectedTab}`;
|
|
153
217
|
}
|
package/src/util/objects.ts
CHANGED
|
@@ -12,28 +12,65 @@ 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>>(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
export function mergeDeep<T extends Record<any, any>, U extends Record<any, any>>(
|
|
16
|
+
target: T,
|
|
17
|
+
source: U,
|
|
18
|
+
ignoreUndefined: boolean = false
|
|
19
|
+
): T & U {
|
|
20
|
+
// If target is not a true object (e.g., null, array, primitive), return target itself.
|
|
21
|
+
if (!isObject(target)) {
|
|
22
|
+
return target as T & U;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Create a shallow copy of the target to avoid modifying the original object.
|
|
26
|
+
const output = { ...target };
|
|
27
|
+
|
|
28
|
+
// If source is not a true object, there's nothing to merge from it.
|
|
29
|
+
// Return the shallow copy of target.
|
|
30
|
+
if (!isObject(source)) {
|
|
31
|
+
return output as T & U;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Iterate over keys in the source object.
|
|
35
|
+
for (const key in source) {
|
|
36
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
37
|
+
const sourceValue = source[key];
|
|
38
|
+
const outputValue = (output as any)[key]; // Current value in our merged object (originating from target)
|
|
39
|
+
|
|
40
|
+
// Skip if source value is undefined and ignoreUndefined is true.
|
|
41
|
+
// This handles both not adding new undefined properties and not overwriting existing properties with undefined.
|
|
42
|
+
if (ignoreUndefined && sourceValue === undefined) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if ((sourceValue as any) instanceof Date) {
|
|
47
|
+
// If source value is a Date, create a new Date instance.
|
|
48
|
+
(output as any)[key] = new Date(sourceValue.getTime());
|
|
49
|
+
} else if (Array.isArray(sourceValue)) {
|
|
50
|
+
// If source value is an array, create a shallow copy of the array.
|
|
51
|
+
(output as any)[key] = [...sourceValue];
|
|
52
|
+
} else if (isObject(sourceValue)) {
|
|
53
|
+
// If source value is an object:
|
|
54
|
+
if (isObject(outputValue)) {
|
|
55
|
+
// If the corresponding value in output (from target) is also an object, recurse.
|
|
56
|
+
// Ensure the ignoreUndefined flag is passed down.
|
|
57
|
+
(output as any)[key] = mergeDeep(outputValue, sourceValue, ignoreUndefined);
|
|
58
|
+
} else {
|
|
59
|
+
// If output's value (from target) is not an object (e.g., null, primitive, or key didn't exist in original target),
|
|
60
|
+
// overwrite with the source object.
|
|
61
|
+
(output as any)[key] = sourceValue;
|
|
62
|
+
}
|
|
29
63
|
} else {
|
|
30
|
-
|
|
64
|
+
// If source value is a primitive, null, or undefined (and not ignored).
|
|
65
|
+
(output as any)[key] = sourceValue;
|
|
31
66
|
}
|
|
32
|
-
}
|
|
67
|
+
}
|
|
33
68
|
}
|
|
34
|
-
|
|
69
|
+
|
|
70
|
+
return output as T & U;
|
|
35
71
|
}
|
|
36
72
|
|
|
73
|
+
|
|
37
74
|
export function getValueInPath(o: object | undefined, path: string): any {
|
|
38
75
|
if (!o) return undefined;
|
|
39
76
|
if (typeof o === "object") {
|
|
@@ -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
|
}
|
package/src/util/resolutions.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ArrayProperty,
|
|
3
|
+
AuthController,
|
|
3
4
|
CMSType,
|
|
4
5
|
CustomizationController,
|
|
5
6
|
EntityCollection,
|
|
@@ -27,9 +28,7 @@ import { getDefaultValuesFor, isPropertyBuilder } from "./entities";
|
|
|
27
28
|
import { DEFAULT_ONE_OF_TYPE } from "./common";
|
|
28
29
|
import { getIn } from "@firecms/formex";
|
|
29
30
|
import { enumToObjectEntries } from "./enums";
|
|
30
|
-
import { isDefaultFieldConfigId } from "../core
|
|
31
|
-
|
|
32
|
-
// import util from "util";
|
|
31
|
+
import { isDefaultFieldConfigId } from "../core";
|
|
33
32
|
|
|
34
33
|
export const resolveCollection = <M extends Record<string, any>, >
|
|
35
34
|
({
|
|
@@ -40,7 +39,8 @@ export const resolveCollection = <M extends Record<string, any>, >
|
|
|
40
39
|
previousValues,
|
|
41
40
|
userConfigPersistence,
|
|
42
41
|
propertyConfigs,
|
|
43
|
-
ignoreMissingFields = false
|
|
42
|
+
ignoreMissingFields = false,
|
|
43
|
+
authController
|
|
44
44
|
}: {
|
|
45
45
|
collection: EntityCollection<M> | ResolvedEntityCollection<M>;
|
|
46
46
|
path: string,
|
|
@@ -50,6 +50,7 @@ export const resolveCollection = <M extends Record<string, any>, >
|
|
|
50
50
|
userConfigPersistence?: UserConfigurationPersistence;
|
|
51
51
|
propertyConfigs?: Record<string, PropertyConfig>;
|
|
52
52
|
ignoreMissingFields?: boolean;
|
|
53
|
+
authController: AuthController;
|
|
53
54
|
}): ResolvedEntityCollection<M> => {
|
|
54
55
|
|
|
55
56
|
const collectionOverride = userConfigPersistence?.getCollectionConfig<M>(path);
|
|
@@ -69,7 +70,8 @@ export const resolveCollection = <M extends Record<string, any>, >
|
|
|
69
70
|
path,
|
|
70
71
|
entityId,
|
|
71
72
|
propertyConfigs: propertyConfigs,
|
|
72
|
-
ignoreMissingFields
|
|
73
|
+
ignoreMissingFields,
|
|
74
|
+
authController
|
|
73
75
|
});
|
|
74
76
|
if (!childResolvedProperty) return {};
|
|
75
77
|
return ({
|
|
@@ -114,6 +116,7 @@ export function resolveProperty<T extends CMSType = CMSType, M extends Record<st
|
|
|
114
116
|
fromBuilder?: boolean;
|
|
115
117
|
propertyConfigs?: Record<string, PropertyConfig<any>>;
|
|
116
118
|
ignoreMissingFields?: boolean;
|
|
119
|
+
authController: AuthController;
|
|
117
120
|
}): ResolvedProperty<T> | null {
|
|
118
121
|
|
|
119
122
|
if (typeof propertyOrBuilder === "object" && "resolved" in propertyOrBuilder) {
|
|
@@ -234,7 +237,8 @@ export function getArrayResolvedProperties<M>({
|
|
|
234
237
|
entityId?: string;
|
|
235
238
|
index?: number;
|
|
236
239
|
fromBuilder?: boolean;
|
|
237
|
-
propertyConfigs?: Record<string, PropertyConfig
|
|
240
|
+
propertyConfigs?: Record<string, PropertyConfig>;
|
|
241
|
+
authController: AuthController;
|
|
238
242
|
}) {
|
|
239
243
|
|
|
240
244
|
const of = property.of;
|
|
@@ -266,6 +270,7 @@ export function resolveArrayProperty<T extends any[], M>({
|
|
|
266
270
|
fromBuilder?: boolean;
|
|
267
271
|
propertyConfigs?: Record<string, PropertyConfig>;
|
|
268
272
|
ignoreMissingFields?: boolean;
|
|
273
|
+
authController: AuthController;
|
|
269
274
|
}): ResolvedArrayProperty {
|
|
270
275
|
const propertyValue = propertyKey ? getIn(props.values, propertyKey) : undefined;
|
|
271
276
|
|
|
@@ -281,7 +286,7 @@ export function resolveArrayProperty<T extends any[], M>({
|
|
|
281
286
|
propertyOrBuilder: p as Property<any>,
|
|
282
287
|
ignoreMissingFields,
|
|
283
288
|
...props,
|
|
284
|
-
index
|
|
289
|
+
index,
|
|
285
290
|
});
|
|
286
291
|
})
|
|
287
292
|
} as ResolvedArrayProperty;
|
|
@@ -373,6 +378,7 @@ export function resolveProperties<M extends Record<string, any>>({
|
|
|
373
378
|
fromBuilder?: boolean;
|
|
374
379
|
propertyConfigs?: Record<string, PropertyConfig>;
|
|
375
380
|
ignoreMissingFields?: boolean;
|
|
381
|
+
authController: AuthController;
|
|
376
382
|
}): ResolvedProperties<M> {
|
|
377
383
|
return Object.entries<PropertyOrBuilder>(properties as Record<string, PropertyOrBuilder>)
|
|
378
384
|
.map(([key, property]) => {
|
|
@@ -435,11 +441,13 @@ export function resolveEntityView(entityView: string | EntityCustomView<any>, co
|
|
|
435
441
|
export function resolvedSelectedEntityView<M extends Record<string, any>>(
|
|
436
442
|
customViews: (string | EntityCustomView<M>)[] | undefined,
|
|
437
443
|
customizationController: CustomizationController,
|
|
438
|
-
selectedTab?: string
|
|
444
|
+
selectedTab?: string,
|
|
445
|
+
canEdit?: boolean,
|
|
439
446
|
) {
|
|
440
447
|
const resolvedEntityViews = customViews ? customViews
|
|
441
448
|
.map(e => resolveEntityView(e, customizationController.entityViews))
|
|
442
|
-
.filter(
|
|
449
|
+
.filter((e): e is EntityCustomView<M> => Boolean(e))
|
|
450
|
+
// .filter((e) => canEdit || !e.includeActions)
|
|
443
451
|
: [];
|
|
444
452
|
|
|
445
453
|
const selectedEntityView = resolvedEntityViews.find(e => e.key === selectedTab);
|
|
@@ -18,6 +18,7 @@ import { PreviewSize } from "../preview";
|
|
|
18
18
|
import { randomString } from "./strings";
|
|
19
19
|
import { resolveStorageFilenameString, resolveStoragePathString } from "./storage";
|
|
20
20
|
import { resolveProperty } from "./resolutions";
|
|
21
|
+
import { useAuthController } from "../hooks";
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Internal representation of an item in the storage
|
|
@@ -34,7 +35,6 @@ export interface StorageFieldItem {
|
|
|
34
35
|
size: PreviewSize
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
38
|
export function useStorageUploadController<M extends object>({
|
|
39
39
|
entityId,
|
|
40
40
|
entityValues,
|
|
@@ -58,6 +58,7 @@ export function useStorageUploadController<M extends object>({
|
|
|
58
58
|
onChange: (value: string | string[] | null) => void
|
|
59
59
|
}) {
|
|
60
60
|
|
|
61
|
+
const authController = useAuthController();
|
|
61
62
|
const storage: StorageConfig | undefined = property.dataType === "string"
|
|
62
63
|
? property.storage
|
|
63
64
|
: property.dataType === "array" &&
|
|
@@ -70,6 +71,8 @@ export function useStorageUploadController<M extends object>({
|
|
|
70
71
|
if (!storage)
|
|
71
72
|
throw Error("Storage meta must be specified");
|
|
72
73
|
|
|
74
|
+
const processFile = storage?.processFile;
|
|
75
|
+
|
|
73
76
|
const metadata: Record<string, any> | undefined = storage?.metadata;
|
|
74
77
|
const size = multipleFilesSupported ? "medium" : "large";
|
|
75
78
|
|
|
@@ -90,7 +93,8 @@ export function useStorageUploadController<M extends object>({
|
|
|
90
93
|
|
|
91
94
|
const resolvedProperty = resolveProperty({
|
|
92
95
|
propertyOrBuilder: property as PropertyOrBuilder,
|
|
93
|
-
values: entityValues
|
|
96
|
+
values: entityValues,
|
|
97
|
+
authController
|
|
94
98
|
}) as ResolvedStringProperty | ResolvedArrayProperty<string[]>;
|
|
95
99
|
|
|
96
100
|
const fileNameBuilder = useCallback(async (file: File) => {
|
|
@@ -170,7 +174,22 @@ export function useStorageUploadController<M extends object>({
|
|
|
170
174
|
if (!acceptedFiles.length || disabled)
|
|
171
175
|
return;
|
|
172
176
|
|
|
177
|
+
if (processFile) {
|
|
178
|
+
try {
|
|
179
|
+
acceptedFiles = await Promise.all(acceptedFiles.map(async file => {
|
|
180
|
+
const processedFile = await processFile(file);
|
|
181
|
+
if (!processedFile) {
|
|
182
|
+
return file;
|
|
183
|
+
}
|
|
184
|
+
return processedFile;
|
|
185
|
+
}));
|
|
186
|
+
} catch (e) {
|
|
187
|
+
console.error("Error processing file with custom code. Attempting to continue uploading.", e);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
173
191
|
let newInternalValue: StorageFieldItem[];
|
|
192
|
+
|
|
174
193
|
if (multipleFilesSupported) {
|
|
175
194
|
newInternalValue = [...internalValue,
|
|
176
195
|
...(await Promise.all(acceptedFiles.map(async file => {
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { RefObject, useCallback, useEffect, useRef } from "react";
|
|
2
|
-
|
|
3
|
-
interface Props {
|
|
4
|
-
onResize: (event: Event) => void;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export const ElementResizeListener: React.FC<Props> = ({ onResize }) => {
|
|
8
|
-
const rafRef = useRef(0);
|
|
9
|
-
const objectRef: RefObject<HTMLObjectElement> = useRef(null);
|
|
10
|
-
const onResizeRef = useRef(onResize);
|
|
11
|
-
|
|
12
|
-
onResizeRef.current = onResize;
|
|
13
|
-
|
|
14
|
-
const _onResize = useCallback((e: Event) => {
|
|
15
|
-
if (rafRef.current) {
|
|
16
|
-
cancelAnimationFrame(rafRef.current);
|
|
17
|
-
}
|
|
18
|
-
rafRef.current = requestAnimationFrame(() => {
|
|
19
|
-
onResizeRef.current(e);
|
|
20
|
-
});
|
|
21
|
-
}, []);
|
|
22
|
-
|
|
23
|
-
const onLoad = useCallback(() => {
|
|
24
|
-
const obj = objectRef.current;
|
|
25
|
-
if (obj && obj.contentDocument && obj.contentDocument.defaultView) {
|
|
26
|
-
obj.contentDocument.defaultView.addEventListener("resize", _onResize);
|
|
27
|
-
}
|
|
28
|
-
}, [_onResize]);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
const obj = objectRef.current;
|
|
32
|
-
return () => {
|
|
33
|
-
if (obj && obj.contentDocument && obj.contentDocument.defaultView) {
|
|
34
|
-
obj.contentDocument.defaultView.removeEventListener("resize", _onResize);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
}, [_onResize]);
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<object
|
|
41
|
-
onLoad={onLoad}
|
|
42
|
-
ref={objectRef}
|
|
43
|
-
tabIndex={-1}
|
|
44
|
-
type={"text/html"}
|
|
45
|
-
data={"about:blank"}
|
|
46
|
-
title={""}
|
|
47
|
-
style={{
|
|
48
|
-
position: "absolute",
|
|
49
|
-
top: 0,
|
|
50
|
-
left: 0,
|
|
51
|
-
height: "100%",
|
|
52
|
-
width: "100%",
|
|
53
|
-
pointerEvents: "none",
|
|
54
|
-
zIndex: -1,
|
|
55
|
-
opacity: 0
|
|
56
|
-
}}
|
|
57
|
-
/>
|
|
58
|
-
);
|
|
59
|
-
};
|