@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.
Files changed (120) hide show
  1. package/dist/app/Scaffold.d.ts +4 -0
  2. package/dist/components/ArrayContainer.d.ts +13 -11
  3. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
  4. package/dist/components/EntityJsonPreview.d.ts +3 -0
  5. package/dist/components/EntityPreview.d.ts +4 -2
  6. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  7. package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
  8. package/dist/components/index.d.ts +1 -0
  9. package/dist/core/DefaultAppBar.d.ts +8 -2
  10. package/dist/core/EntityEditView.d.ts +8 -1
  11. package/dist/core/FireCMS.d.ts +2 -2
  12. package/dist/form/EntityForm.d.ts +5 -1
  13. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  14. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  15. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
  16. package/dist/hooks/data/save.d.ts +1 -1
  17. package/dist/hooks/useBuildNavigationController.d.ts +2 -9
  18. package/dist/index.es.js +9251 -8721
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.umd.js +16686 -16160
  21. package/dist/index.umd.js.map +1 -1
  22. package/dist/internal/useBuildDataSource.d.ts +3 -2
  23. package/dist/internal/useBuildSideEntityController.d.ts +3 -3
  24. package/dist/types/collections.d.ts +26 -2
  25. package/dist/types/fields.d.ts +4 -1
  26. package/dist/types/firecms.d.ts +3 -2
  27. package/dist/types/navigation.d.ts +9 -0
  28. package/dist/types/plugins.d.ts +13 -0
  29. package/dist/types/properties.d.ts +22 -1
  30. package/dist/types/side_entity_controller.d.ts +4 -0
  31. package/dist/util/callbacks.d.ts +2 -0
  32. package/dist/util/createFormexStub.d.ts +2 -0
  33. package/dist/util/index.d.ts +1 -0
  34. package/dist/util/navigation_utils.d.ts +2 -1
  35. package/dist/util/objects.d.ts +1 -1
  36. package/dist/util/property_utils.d.ts +2 -2
  37. package/dist/util/references.d.ts +2 -2
  38. package/dist/util/resolutions.d.ts +11 -6
  39. package/package.json +9 -6
  40. package/src/app/Scaffold.tsx +13 -3
  41. package/src/components/ArrayContainer.tsx +414 -282
  42. package/src/components/ClearFilterSortButton.tsx +1 -1
  43. package/src/components/ConfirmationDialog.tsx +9 -9
  44. package/src/components/DeleteEntityDialog.tsx +4 -2
  45. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +8 -6
  46. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +1 -1
  47. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +1 -1
  48. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
  49. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +58 -29
  50. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
  51. package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -6
  52. package/src/components/EntityJsonPreview.tsx +66 -0
  53. package/src/components/EntityPreview.tsx +27 -16
  54. package/src/components/EntityView.tsx +4 -1
  55. package/src/components/ErrorView.tsx +1 -1
  56. package/src/components/HomePage/DefaultHomePage.tsx +2 -1
  57. package/src/components/HomePage/NavigationCardBinding.tsx +3 -1
  58. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -1
  59. package/src/components/SelectableTable/SelectableTable.tsx +140 -143
  60. package/src/components/VirtualTable/VirtualTable.tsx +37 -31
  61. package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
  62. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
  63. package/src/components/common/default_entity_actions.tsx +15 -4
  64. package/src/components/index.tsx +2 -0
  65. package/src/core/DefaultAppBar.tsx +17 -5
  66. package/src/core/EntityEditView.tsx +135 -47
  67. package/src/core/EntitySidePanel.tsx +15 -20
  68. package/src/core/FireCMS.tsx +19 -12
  69. package/src/form/EntityForm.tsx +32 -24
  70. package/src/form/PropertyFieldBinding.tsx +8 -6
  71. package/src/form/components/CustomIdField.tsx +3 -1
  72. package/src/form/components/LabelWithIcon.tsx +1 -1
  73. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +4 -1
  74. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -2
  75. package/src/form/field_bindings/BlockFieldBinding.tsx +1 -1
  76. package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
  77. package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
  78. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +6 -2
  79. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +3 -3
  80. package/src/form/field_bindings/RepeatFieldBinding.tsx +9 -2
  81. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -2
  82. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +227 -156
  83. package/src/hooks/data/save.ts +24 -35
  84. package/src/hooks/useBuildNavigationController.tsx +42 -17
  85. package/src/hooks/useFireCMSContext.tsx +0 -30
  86. package/src/internal/useBuildDataSource.ts +9 -5
  87. package/src/internal/useBuildSideEntityController.tsx +26 -20
  88. package/src/preview/PropertyPreview.tsx +4 -2
  89. package/src/preview/components/ImagePreview.tsx +2 -2
  90. package/src/preview/components/ReferencePreview.tsx +1 -1
  91. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +5 -4
  92. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +4 -3
  93. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +4 -2
  94. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -3
  95. package/src/preview/property_previews/ArrayOneOfPreview.tsx +4 -2
  96. package/src/preview/property_previews/ArrayPropertyPreview.tsx +4 -2
  97. package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
  98. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
  99. package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
  100. package/src/routes/FireCMSRoute.tsx +15 -4
  101. package/src/types/collections.ts +31 -2
  102. package/src/types/fields.tsx +5 -1
  103. package/src/types/firecms.tsx +3 -3
  104. package/src/types/navigation.ts +11 -0
  105. package/src/types/plugins.tsx +17 -0
  106. package/src/types/properties.ts +27 -1
  107. package/src/types/side_entity_controller.tsx +5 -0
  108. package/src/util/callbacks.ts +119 -0
  109. package/src/util/createFormexStub.tsx +62 -0
  110. package/src/util/index.ts +1 -0
  111. package/src/util/join_collections.ts +3 -1
  112. package/src/util/navigation_from_path.ts +5 -1
  113. package/src/util/navigation_utils.ts +84 -20
  114. package/src/util/objects.ts +54 -17
  115. package/src/util/property_utils.tsx +7 -3
  116. package/src/util/references.ts +8 -6
  117. package/src/util/resolutions.ts +17 -9
  118. package/src/util/useStorageUploadController.tsx +21 -2
  119. package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
  120. 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
- const cleanPath = removeInitialAndTrailingSlashes(path);
37
- const subpaths = cleanPath.split("/");
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
- const aliasedCollection = allCollections.find((col) => col.id === subpaths[0]);
43
- let resolvedAliased;
44
- if (aliasedCollection) {
45
- resolvedAliased = aliasedCollection.path;
46
- }
40
+ let currentCollections: EntityCollection[] | undefined = allCollections;
41
+ const resolvedPathParts: string[] = [];
47
42
 
48
- if (subpaths.length > 1) {
49
- const segmentCollection = getCollectionByPathOrId(resolvedAliased ?? subpaths[0], allCollections);
50
- if (!segmentCollection?.subcollections) {
51
- return cleanPath;
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
  }
@@ -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>>(target: T, source: U): T & U {
16
- const targetIsObject = isObject(target);
17
- const output = targetIsObject ? { ...target } : target;
18
- if (targetIsObject && isObject(source)) {
19
- Object.keys(source).forEach(key => {
20
- const sourceElement = source[key];
21
- if (sourceElement instanceof Date) {
22
- // Assign a new Date instance with the same time value
23
- Object.assign(output, { [key]: new Date(sourceElement.getTime()) });
24
- } else if (isObject(sourceElement)) {
25
- if (!(key in target))
26
- Object.assign(output, { [key]: sourceElement });
27
- else
28
- (output as any)[key] = mergeDeep((target as any)[key], sourceElement);
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
- Object.assign(output, { [key]: sourceElement });
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
- return output as T;
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(propertyOrBuilder: PropertyOrBuilder,
17
- fields: Record<string, PropertyConfig>) {
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") {
@@ -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(targetCollection: EntityCollection<any>,
7
- fields: Record<string, PropertyConfig>,
8
- previewProperties?: string[],
9
- limit = 3) {
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
  }
@@ -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/field_configs";
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(Boolean) as EntityCustomView[]
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,5 +0,0 @@
1
- interface Props {
2
- onResize: (event: Event) => void;
3
- }
4
- export declare const ElementResizeListener: React.FC<Props>;
5
- export {};
@@ -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
- };