@firecms/core 3.0.0-beta.13 → 3.0.0-beta.15

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 (124) hide show
  1. package/dist/components/ArrayContainer.d.ts +7 -12
  2. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
  3. package/dist/components/EntityPreview.d.ts +4 -2
  4. package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
  5. package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
  6. package/dist/components/HomePage/NavigationCard.d.ts +3 -1
  7. package/dist/components/HomePage/NavigationCardBinding.d.ts +3 -2
  8. package/dist/components/HomePage/NavigationGroup.d.ts +7 -1
  9. package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
  10. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  11. package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
  12. package/dist/components/index.d.ts +1 -0
  13. package/dist/core/EntityEditView.d.ts +3 -0
  14. package/dist/core/EntityEditViewFormActions.d.ts +1 -1
  15. package/dist/core/field_configs.d.ts +1 -1
  16. package/dist/form/EntityForm.d.ts +2 -1
  17. package/dist/form/EntityFormActions.d.ts +6 -2
  18. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  19. package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
  20. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
  21. package/dist/form/index.d.ts +1 -0
  22. package/dist/hooks/useBuildNavigationController.d.ts +50 -8
  23. package/dist/index.es.js +3259 -1649
  24. package/dist/index.es.js.map +1 -1
  25. package/dist/index.umd.js +3265 -1658
  26. package/dist/index.umd.js.map +1 -1
  27. package/dist/types/analytics.d.ts +1 -1
  28. package/dist/types/collections.d.ts +28 -4
  29. package/dist/types/customization_controller.d.ts +8 -0
  30. package/dist/types/entity_actions.d.ts +46 -6
  31. package/dist/types/firecms.d.ts +10 -1
  32. package/dist/types/navigation.d.ts +26 -5
  33. package/dist/types/plugins.d.ts +32 -1
  34. package/dist/types/properties.d.ts +7 -0
  35. package/dist/types/property_config.d.ts +1 -1
  36. package/dist/types/side_entity_controller.d.ts +8 -0
  37. package/dist/util/callbacks.d.ts +2 -0
  38. package/dist/util/icons.d.ts +2 -2
  39. package/dist/util/index.d.ts +1 -0
  40. package/dist/util/navigation_from_path.d.ts +4 -0
  41. package/dist/util/resolutions.d.ts +2 -1
  42. package/package.json +8 -6
  43. package/src/components/ArrayContainer.tsx +409 -294
  44. package/src/components/ClearFilterSortButton.tsx +1 -1
  45. package/src/components/ConfirmationDialog.tsx +10 -9
  46. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +6 -0
  47. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +26 -4
  48. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
  49. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
  50. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +53 -27
  51. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
  52. package/src/components/EntityCollectionView/EntityCollectionView.tsx +11 -4
  53. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +3 -2
  54. package/src/components/EntityPreview.tsx +18 -14
  55. package/src/components/ErrorView.tsx +1 -1
  56. package/src/components/FireCMSLogo.tsx +7 -51
  57. package/src/components/HomePage/DefaultHomePage.tsx +491 -156
  58. package/src/components/HomePage/FavouritesView.tsx +3 -3
  59. package/src/components/HomePage/HomePageDnD.tsx +599 -0
  60. package/src/components/HomePage/NavigationCard.tsx +47 -38
  61. package/src/components/HomePage/NavigationCardBinding.tsx +13 -7
  62. package/src/components/HomePage/NavigationGroup.tsx +63 -29
  63. package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
  64. package/src/components/SelectableTable/SelectableTable.tsx +140 -143
  65. package/src/components/UnsavedChangesDialog.tsx +6 -2
  66. package/src/components/VirtualTable/VirtualTable.tsx +8 -30
  67. package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
  68. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
  69. package/src/components/common/default_entity_actions.tsx +25 -9
  70. package/src/components/common/useDataSourceTableController.tsx +2 -2
  71. package/src/components/index.tsx +2 -0
  72. package/src/core/DefaultDrawer.tsx +8 -8
  73. package/src/core/DrawerNavigationItem.tsx +1 -1
  74. package/src/core/EntityEditView.tsx +65 -18
  75. package/src/core/EntityEditViewFormActions.tsx +154 -29
  76. package/src/core/EntitySidePanel.tsx +19 -21
  77. package/src/core/FireCMS.tsx +9 -1
  78. package/src/core/field_configs.tsx +15 -1
  79. package/src/form/EntityForm.tsx +37 -5
  80. package/src/form/EntityFormActions.tsx +51 -9
  81. package/src/form/PropertyFieldBinding.tsx +0 -1
  82. package/src/form/components/CustomIdField.tsx +3 -1
  83. package/src/form/components/LabelWithIcon.tsx +1 -1
  84. package/src/form/components/StorageItemPreview.tsx +1 -1
  85. package/src/form/components/StorageUploadProgress.tsx +3 -3
  86. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -2
  87. package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
  88. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -2
  89. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
  90. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
  91. package/src/form/field_bindings/SelectFieldBinding.tsx +2 -1
  92. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +216 -153
  93. package/src/form/index.tsx +1 -0
  94. package/src/hooks/data/save.ts +0 -6
  95. package/src/hooks/useBuildNavigationController.tsx +299 -96
  96. package/src/internal/useBuildSideEntityController.tsx +8 -5
  97. package/src/preview/PropertyPreview.tsx +14 -0
  98. package/src/preview/components/ImagePreview.tsx +2 -2
  99. package/src/preview/components/ReferencePreview.tsx +1 -1
  100. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
  101. package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
  102. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
  103. package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
  104. package/src/routes/FireCMSRoute.tsx +3 -3
  105. package/src/types/analytics.ts +3 -0
  106. package/src/types/collections.ts +32 -4
  107. package/src/types/customization_controller.tsx +9 -0
  108. package/src/types/entity_actions.tsx +57 -6
  109. package/src/types/firecms.tsx +11 -2
  110. package/src/types/navigation.ts +34 -6
  111. package/src/types/plugins.tsx +40 -1
  112. package/src/types/properties.ts +8 -0
  113. package/src/types/property_config.tsx +1 -0
  114. package/src/types/side_entity_controller.tsx +10 -5
  115. package/src/util/callbacks.ts +119 -0
  116. package/src/util/icons.tsx +22 -7
  117. package/src/util/index.ts +1 -0
  118. package/src/util/join_collections.ts +3 -1
  119. package/src/util/navigation_from_path.ts +15 -5
  120. package/src/util/navigation_utils.ts +72 -57
  121. package/src/util/objects.ts +53 -20
  122. package/src/util/resolutions.ts +13 -1
  123. package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
  124. package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
@@ -4,13 +4,28 @@ import { coolIconKeys, Icon, IconColor, iconKeys } from "@firecms/ui";
4
4
  import { slugify } from "./strings";
5
5
  import equal from "react-fast-compare"
6
6
 
7
- export function getIcon(iconKey?: string, className?: string, color?:IconColor): React.ReactElement | undefined {
7
+ export function getIcon(iconKey?: string | React.ReactNode,
8
+ className?: string,
9
+ color?: IconColor,
10
+ size?: "smallest" | "small" | "medium" | "large" | number,): React.ReactElement | undefined {
11
+
12
+ if (React.isValidElement(iconKey)) {
13
+ return iconKey;
14
+ }
15
+
8
16
  if (!iconKey) return undefined;
9
- iconKey = slugify(iconKey);
10
- if (!(iconKey in iconKeysMap)) {
11
- return undefined;
17
+ if (typeof iconKey === "string") {
18
+
19
+ const usedIconKey = slugify(iconKey);
20
+ if (!(usedIconKey in iconKeysMap)) {
21
+ return undefined;
22
+ }
23
+ return usedIconKey in iconKeysMap ?
24
+ <Icon iconKey={usedIconKey} size={size} className={className} color={color}/> : undefined;
12
25
  }
13
- return iconKey in iconKeysMap ? <Icon iconKey={iconKey} size={"medium"} className={className} color={color}/> : undefined;
26
+
27
+ console.warn("Invalid icon key provided:", iconKey);
28
+ return undefined;
14
29
  }
15
30
 
16
31
  export type IconViewProps = {
@@ -18,7 +33,7 @@ export type IconViewProps = {
18
33
  name: string;
19
34
  singularName?: string;
20
35
  group?: string;
21
- icon?: string;
36
+ icon?: string | React.ReactNode;
22
37
  }
23
38
 
24
39
  export const IconForView = React.memo(
@@ -34,7 +49,7 @@ export const IconForView = React.memo(
34
49
  size?: "smallest" | "small" | "medium" | "large" | number,
35
50
  }): React.ReactElement {
36
51
  if (!collectionOrView) return <></>;
37
- const icon = getIcon(collectionOrView.icon, className, color);
52
+ const icon = getIcon(collectionOrView.icon, className, color, size);
38
53
  if (collectionOrView?.icon && icon)
39
54
  return icon;
40
55
 
package/src/util/index.ts CHANGED
@@ -23,3 +23,4 @@ export * from "./join_collections";
23
23
  export * from "./builders";
24
24
  export * from "./useTraceUpdate";
25
25
  export * from "./storage";
26
+ export * from "./callbacks";
@@ -100,13 +100,15 @@ export function mergeCollection(target: EntityCollection,
100
100
  const sourcePropertiesOrder = getCollectionKeys(source);
101
101
  const mergedPropertiesOrder = [...new Set([...sourcePropertiesOrder, ...targetPropertiesOrder])];
102
102
  const mergedEntityViews = [...new Set([...(target.entityViews ?? []), ...(source.entityViews ?? [])])];
103
+ const mergedEntityActions = [...new Set([...(target.entityActions ?? []), ...(source.entityActions ?? [])])];
103
104
 
104
105
  let resultCollection: EntityCollection = {
105
106
  ...mergedCollection,
106
107
  subcollections: subcollectionsMerged,
107
108
  properties: sortProperties(propertiesMerged, mergedPropertiesOrder),
108
109
  propertiesOrder: mergedPropertiesOrder,
109
- entityViews: mergedEntityViews
110
+ entityViews: mergedEntityViews,
111
+ entityActions: mergedEntityActions,
110
112
  };
111
113
  if (modifyCollection) {
112
114
  const modifiedCollection = modifyCollection({
@@ -11,6 +11,7 @@ export interface NavigationViewEntityInternal<M extends Record<string, any>> {
11
11
  type: "entity";
12
12
  entityId: string;
13
13
  path: string;
14
+ fullIdPath: string;
14
15
  fullPath: string;
15
16
  parentCollection: EntityCollection<M>;
16
17
  }
@@ -19,6 +20,7 @@ export interface NavigationViewCollectionInternal<M extends Record<string, any>>
19
20
  type: "collection";
20
21
  id: string;
21
22
  path: string;
23
+ fullIdPath: string;
22
24
  fullPath: string;
23
25
  collection: EntityCollection<M>;
24
26
  }
@@ -26,6 +28,7 @@ export interface NavigationViewCollectionInternal<M extends Record<string, any>>
26
28
  export interface NavigationViewEntityCustomInternal<M extends Record<string, any>> {
27
29
  type: "custom_view";
28
30
  path: string;
31
+ fullIdPath: string;
29
32
  fullPath: string;
30
33
  entityId: string;
31
34
  view: EntityCustomView<M>;
@@ -35,13 +38,15 @@ export function getNavigationEntriesFromPath(props: {
35
38
  path: string,
36
39
  collections: EntityCollection[] | undefined,
37
40
  currentFullPath?: string,
41
+ currentFullIdPath?: string,
38
42
  contextEntityViews?: EntityCustomView<any>[]
39
43
  }): NavigationViewInternal [] {
40
44
 
41
45
  const {
42
46
  path,
43
47
  collections = [],
44
- currentFullPath
48
+ currentFullPath,
49
+ currentFullIdPath
45
50
  } = props;
46
51
 
47
52
  const subpaths = removeInitialAndTrailingSlashes(path).split("/");
@@ -58,16 +63,18 @@ export function getNavigationEntriesFromPath(props: {
58
63
  }
59
64
 
60
65
  if (collection) {
61
- const pathOrAlias = collection.id ?? collection.path;
62
66
  const collectionPath = currentFullPath && currentFullPath.length > 0
63
- ? (currentFullPath + "/" + pathOrAlias)
64
- : pathOrAlias;
65
-
67
+ ? (currentFullPath + "/" + collection.path)
68
+ : collection.path;
69
+ const fullIdPath = currentFullIdPath && currentFullIdPath.length > 0
70
+ ? (currentFullIdPath + "/" + collection.id)
71
+ : collection.id;
66
72
  result.push({
67
73
  type: "collection",
68
74
  id: collection.id,
69
75
  path: collectionPath,
70
76
  fullPath: collectionPath,
77
+ fullIdPath,
71
78
  collection
72
79
  });
73
80
  const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
@@ -79,6 +86,7 @@ export function getNavigationEntriesFromPath(props: {
79
86
  type: "entity",
80
87
  entityId,
81
88
  path: collectionPath,
89
+ fullIdPath,
82
90
  fullPath: fullPath,
83
91
  parentCollection: collection
84
92
  });
@@ -97,6 +105,7 @@ export function getNavigationEntriesFromPath(props: {
97
105
  type: "custom_view",
98
106
  path: collectionPath,
99
107
  entityId: entityId,
108
+ fullIdPath,
100
109
  fullPath: fullPath + "/" + customView.key,
101
110
  view: customView
102
111
  });
@@ -105,6 +114,7 @@ export function getNavigationEntriesFromPath(props: {
105
114
  path: newPath,
106
115
  collections: collection.subcollections,
107
116
  currentFullPath: fullPath,
117
+ currentFullIdPath: fullIdPath,
108
118
  contextEntityViews: props.contextEntityViews
109
119
  }));
110
120
  }
@@ -32,75 +32,90 @@ export function getLastSegment(path: string) {
32
32
  }
33
33
 
34
34
  export function resolveCollectionPathIds(path: string, allCollections: EntityCollection[]): string {
35
- const cleanPath = removeInitialAndTrailingSlashes(path);
36
- const subpaths = cleanPath.split("/");
37
-
38
- if (subpaths.length % 2 === 0) {
39
- throw Error(`resolveCollectionPathIds: Collection paths must have an odd number of segments: ${path}`);
40
- }
41
-
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;
35
+ let remainingPath = removeInitialAndTrailingSlashes(path);
36
+ if (!remainingPath) {
37
+ return "";
46
38
  }
47
39
 
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
- }
40
+ let currentCollections: EntityCollection[] | undefined = allCollections;
41
+ const resolvedPathParts: string[] = [];
53
42
 
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;
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
65
49
  break;
66
50
  }
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
51
 
75
- if (!matchingCollections.length) {
76
- return cleanPath;
77
- }
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
+ }
78
79
 
79
- matchingCollection = matchingCollections[0];
80
- }
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
+ }
81
94
 
82
- const entityId = subpaths[entityIndex];
83
- const remainingPath = subpaths.slice(entityIndex + 1);
95
+ resolvedPathParts.push(entityId); // Append entity ID
96
+ currentCollections = foundCollection.subcollections; // Move to subcollections
97
+ foundMatch = true;
84
98
 
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
- );
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
+ }
91
106
 
92
- if (subcollection) {
93
- return `${matchingCollection.path}/${entityId}/${subcollection.path}`;
94
107
  }
95
- }
96
108
 
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}`;
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;
115
+ }
100
116
  }
101
117
 
102
- // Default case - couldn't match subcollection
103
- return `${matchingCollection.path}/${entityId}/${remainingPath.join("/")}`;
118
+ return resolvedPathParts.join("/");
104
119
  }
105
120
 
106
121
  /**
@@ -186,8 +201,8 @@ export function navigateToEntity({
186
201
 
187
202
  sideEntityController.open({
188
203
  entityId,
189
- path: fullIdPath ?? path,
190
- // fullIdPath,
204
+ path,
205
+ fullIdPath,
191
206
  copy,
192
207
  selectedTab,
193
208
  collection,
@@ -12,32 +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, ignoreUndefined: boolean = false): 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
- // Skip undefined values when ignoreUndefined is true
22
- if (ignoreUndefined && sourceElement === undefined) {
23
- return;
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;
24
44
  }
25
- if (sourceElement instanceof Date) {
26
- // Assign a new Date instance with the same time value
27
- Object.assign(output, { [key]: new Date(sourceElement.getTime()) });
28
- } else if (isObject(sourceElement)) {
29
- if (!(key in target))
30
- Object.assign(output, { [key]: sourceElement });
31
- else
32
- (output as any)[key] = mergeDeep((target as any)[key], sourceElement);
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
+ }
33
63
  } else {
34
- Object.assign(output, { [key]: sourceElement });
64
+ // If source value is a primitive, null, or undefined (and not ignored).
65
+ (output as any)[key] = sourceValue;
35
66
  }
36
- });
67
+ }
37
68
  }
38
- return output as T;
69
+
70
+ return output as T & U;
39
71
  }
40
72
 
73
+
41
74
  export function getValueInPath(o: object | undefined, path: string): any {
42
75
  if (!o) return undefined;
43
76
  if (typeof o === "object") {
@@ -3,6 +3,7 @@ import {
3
3
  AuthController,
4
4
  CMSType,
5
5
  CustomizationController,
6
+ EntityAction,
6
7
  EntityCollection,
7
8
  EntityCustomView,
8
9
  EntityValues,
@@ -438,6 +439,17 @@ export function resolveEntityView(entityView: string | EntityCustomView<any>, co
438
439
  }
439
440
  }
440
441
 
442
+ export function resolveEntityAction<M extends Record<string, any>>(
443
+ entityAction: string | EntityAction<M>,
444
+ contextEntityActions?: EntityAction<M>[]
445
+ ): EntityAction<M> | undefined {
446
+ if (typeof entityAction === "string") {
447
+ return contextEntityActions?.find((entry) => entry.key === entityAction);
448
+ } else {
449
+ return entityAction;
450
+ }
451
+ }
452
+
441
453
  export function resolvedSelectedEntityView<M extends Record<string, any>>(
442
454
  customViews: (string | EntityCustomView<M>)[] | undefined,
443
455
  customizationController: CustomizationController,
@@ -447,7 +459,7 @@ export function resolvedSelectedEntityView<M extends Record<string, any>>(
447
459
  const resolvedEntityViews = customViews ? customViews
448
460
  .map(e => resolveEntityView(e, customizationController.entityViews))
449
461
  .filter((e): e is EntityCustomView<M> => Boolean(e))
450
- // .filter((e) => canEdit || !e.includeActions)
462
+ // .filter((e) => canEdit || !e.includeActions)
451
463
  : [];
452
464
 
453
465
  const selectedEntityView = resolvedEntityViews.find(e => e.key === selectedTab);
@@ -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
- };