@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.
- package/dist/components/ArrayContainer.d.ts +7 -12
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
- package/dist/components/EntityPreview.d.ts +4 -2
- package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
- package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
- package/dist/components/HomePage/NavigationCard.d.ts +3 -1
- package/dist/components/HomePage/NavigationCardBinding.d.ts +3 -2
- package/dist/components/HomePage/NavigationGroup.d.ts +7 -1
- package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
- 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/EntityEditView.d.ts +3 -0
- package/dist/core/EntityEditViewFormActions.d.ts +1 -1
- package/dist/core/field_configs.d.ts +1 -1
- package/dist/form/EntityForm.d.ts +2 -1
- package/dist/form/EntityFormActions.d.ts +6 -2
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
- package/dist/form/index.d.ts +1 -0
- package/dist/hooks/useBuildNavigationController.d.ts +50 -8
- package/dist/index.es.js +3259 -1649
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +3265 -1658
- package/dist/index.umd.js.map +1 -1
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/collections.d.ts +28 -4
- package/dist/types/customization_controller.d.ts +8 -0
- package/dist/types/entity_actions.d.ts +46 -6
- package/dist/types/firecms.d.ts +10 -1
- package/dist/types/navigation.d.ts +26 -5
- package/dist/types/plugins.d.ts +32 -1
- package/dist/types/properties.d.ts +7 -0
- package/dist/types/property_config.d.ts +1 -1
- package/dist/types/side_entity_controller.d.ts +8 -0
- package/dist/util/callbacks.d.ts +2 -0
- package/dist/util/icons.d.ts +2 -2
- package/dist/util/index.d.ts +1 -0
- package/dist/util/navigation_from_path.d.ts +4 -0
- package/dist/util/resolutions.d.ts +2 -1
- package/package.json +8 -6
- package/src/components/ArrayContainer.tsx +409 -294
- package/src/components/ClearFilterSortButton.tsx +1 -1
- package/src/components/ConfirmationDialog.tsx +10 -9
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +6 -0
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +26 -4
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +53 -27
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +11 -4
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +3 -2
- package/src/components/EntityPreview.tsx +18 -14
- package/src/components/ErrorView.tsx +1 -1
- package/src/components/FireCMSLogo.tsx +7 -51
- package/src/components/HomePage/DefaultHomePage.tsx +491 -156
- package/src/components/HomePage/FavouritesView.tsx +3 -3
- package/src/components/HomePage/HomePageDnD.tsx +599 -0
- package/src/components/HomePage/NavigationCard.tsx +47 -38
- package/src/components/HomePage/NavigationCardBinding.tsx +13 -7
- package/src/components/HomePage/NavigationGroup.tsx +63 -29
- package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
- package/src/components/SelectableTable/SelectableTable.tsx +140 -143
- package/src/components/UnsavedChangesDialog.tsx +6 -2
- package/src/components/VirtualTable/VirtualTable.tsx +8 -30
- 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 +25 -9
- package/src/components/common/useDataSourceTableController.tsx +2 -2
- package/src/components/index.tsx +2 -0
- package/src/core/DefaultDrawer.tsx +8 -8
- package/src/core/DrawerNavigationItem.tsx +1 -1
- package/src/core/EntityEditView.tsx +65 -18
- package/src/core/EntityEditViewFormActions.tsx +154 -29
- package/src/core/EntitySidePanel.tsx +19 -21
- package/src/core/FireCMS.tsx +9 -1
- package/src/core/field_configs.tsx +15 -1
- package/src/form/EntityForm.tsx +37 -5
- package/src/form/EntityFormActions.tsx +51 -9
- package/src/form/PropertyFieldBinding.tsx +0 -1
- package/src/form/components/CustomIdField.tsx +3 -1
- package/src/form/components/LabelWithIcon.tsx +1 -1
- package/src/form/components/StorageItemPreview.tsx +1 -1
- package/src/form/components/StorageUploadProgress.tsx +3 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -2
- package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -2
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
- package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
- package/src/form/field_bindings/SelectFieldBinding.tsx +2 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +216 -153
- package/src/form/index.tsx +1 -0
- package/src/hooks/data/save.ts +0 -6
- package/src/hooks/useBuildNavigationController.tsx +299 -96
- package/src/internal/useBuildSideEntityController.tsx +8 -5
- package/src/preview/PropertyPreview.tsx +14 -0
- package/src/preview/components/ImagePreview.tsx +2 -2
- package/src/preview/components/ReferencePreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
- 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 +3 -3
- package/src/types/analytics.ts +3 -0
- package/src/types/collections.ts +32 -4
- package/src/types/customization_controller.tsx +9 -0
- package/src/types/entity_actions.tsx +57 -6
- package/src/types/firecms.tsx +11 -2
- package/src/types/navigation.ts +34 -6
- package/src/types/plugins.tsx +40 -1
- package/src/types/properties.ts +8 -0
- package/src/types/property_config.tsx +1 -0
- package/src/types/side_entity_controller.tsx +10 -5
- package/src/util/callbacks.ts +119 -0
- package/src/util/icons.tsx +22 -7
- package/src/util/index.ts +1 -0
- package/src/util/join_collections.ts +3 -1
- package/src/util/navigation_from_path.ts +15 -5
- package/src/util/navigation_utils.ts +72 -57
- package/src/util/objects.ts +53 -20
- package/src/util/resolutions.ts +13 -1
- package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
- package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
package/src/util/icons.tsx
CHANGED
|
@@ -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
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
@@ -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 + "/" +
|
|
64
|
-
:
|
|
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
|
-
|
|
36
|
-
|
|
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
|
-
|
|
49
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
95
|
+
resolvedPathParts.push(entityId); // Append entity ID
|
|
96
|
+
currentCollections = foundCollection.subcollections; // Move to subcollections
|
|
97
|
+
foundMatch = true;
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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
|
|
190
|
-
|
|
204
|
+
path,
|
|
205
|
+
fullIdPath,
|
|
191
206
|
copy,
|
|
192
207
|
selectedTab,
|
|
193
208
|
collection,
|
package/src/util/objects.ts
CHANGED
|
@@ -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>>(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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") {
|
package/src/util/resolutions.ts
CHANGED
|
@@ -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
|
-
|
|
462
|
+
// .filter((e) => canEdit || !e.includeActions)
|
|
451
463
|
: [];
|
|
452
464
|
|
|
453
465
|
const selectedEntityView = resolvedEntityViews.find(e => e.key === selectedTab);
|
|
@@ -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
|
-
};
|