@firecms/core 3.0.0-beta.11 → 3.0.0-beta.12
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 -3
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +15 -1
- package/dist/components/EntityCollectionTable/index.d.ts +1 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
- package/dist/components/PropertyIdCopyTooltip.d.ts +1 -1
- package/dist/components/SelectableTable/SelectableTable.d.ts +12 -2
- package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +1 -1
- package/dist/components/UnsavedChangesDialog.d.ts +8 -0
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -0
- package/dist/components/common/default_entity_actions.d.ts +0 -2
- package/dist/components/common/index.d.ts +1 -1
- package/dist/components/common/useColumnsIds.d.ts +1 -0
- package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +10 -2
- package/dist/components/common/useDebouncedCallback.d.ts +1 -0
- package/dist/components/common/useScrollRestoration.d.ts +14 -0
- package/dist/contexts/BreacrumbsContext.d.ts +8 -0
- package/dist/core/EntityEditView.d.ts +29 -21
- package/dist/core/EntityEditViewFormActions.d.ts +2 -0
- package/dist/core/FireCMSRouter.d.ts +4 -0
- package/dist/core/NavigationRoutes.d.ts +0 -1
- package/dist/core/SideDialogs.d.ts +4 -2
- package/dist/core/index.d.ts +2 -1
- package/dist/form/EntityForm.d.ts +45 -0
- package/dist/form/EntityFormActions.d.ts +17 -0
- package/dist/form/PropertyFieldBinding.d.ts +1 -1
- package/dist/form/components/FormEntry.d.ts +6 -0
- package/dist/form/components/FormLayout.d.ts +5 -0
- package/dist/form/components/index.d.ts +2 -0
- package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
- package/dist/form/index.d.ts +16 -16
- package/dist/hooks/data/useEntityFetch.d.ts +2 -1
- package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
- package/dist/hooks/useBuildNavigationController.d.ts +4 -1
- package/dist/hooks/useModeController.d.ts +1 -2
- package/dist/index.es.js +18213 -16819
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +18164 -16771
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildSideEntityController.d.ts +2 -2
- package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
- package/dist/routes/CustomCMSRoute.d.ts +4 -0
- package/dist/routes/FireCMSRoute.d.ts +1 -0
- package/dist/routes/HomePageRoute.d.ts +3 -0
- package/dist/types/collections.d.ts +30 -10
- package/dist/types/datasource.d.ts +2 -2
- package/dist/types/dialogs_controller.d.ts +7 -3
- package/dist/types/entities.d.ts +1 -1
- package/dist/types/entity_actions.d.ts +10 -0
- package/dist/types/fields.d.ts +9 -2
- package/dist/types/navigation.d.ts +29 -12
- package/dist/types/plugins.d.ts +1 -0
- package/dist/types/properties.d.ts +18 -3
- package/dist/types/side_entity_controller.d.ts +6 -1
- package/dist/util/builders.d.ts +1 -1
- package/dist/util/entity_actions.d.ts +2 -0
- package/dist/util/entity_cache.d.ts +23 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/navigation_from_path.d.ts +6 -1
- package/dist/util/navigation_utils.d.ts +12 -1
- package/dist/util/resolutions.d.ts +6 -1
- package/package.json +19 -21
- package/src/components/ArrayContainer.tsx +48 -29
- package/src/components/CircularProgressCenter.tsx +1 -1
- package/src/components/DeleteEntityDialog.tsx +1 -1
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +40 -19
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -4
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +18 -1
- package/src/components/EntityCollectionTable/index.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +5 -11
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +83 -41
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -2
- package/src/components/NotFoundPage.tsx +2 -2
- package/src/components/PropertyIdCopyTooltip.tsx +2 -3
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +11 -6
- package/src/components/SelectableTable/SelectableTable.tsx +18 -3
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +7 -7
- package/src/components/UnsavedChangesDialog.tsx +42 -0
- package/src/components/VirtualTable/VirtualTable.tsx +19 -2
- package/src/components/VirtualTable/VirtualTableProps.tsx +15 -0
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +1 -1
- package/src/components/common/default_entity_actions.tsx +26 -37
- package/src/components/common/index.ts +1 -1
- package/src/components/common/useColumnsIds.tsx +1 -1
- package/src/components/common/useDataSourceTableController.tsx +420 -0
- package/src/components/common/useDebouncedCallback.tsx +20 -0
- package/src/components/common/useScrollRestoration.tsx +68 -0
- package/src/contexts/BreacrumbsContext.tsx +38 -0
- package/src/contexts/DialogsProvider.tsx +3 -2
- package/src/contexts/ModeController.tsx +1 -3
- package/src/core/DefaultAppBar.tsx +44 -14
- package/src/core/DefaultDrawer.tsx +1 -1
- package/src/core/EntityEditView.tsx +278 -1000
- package/src/core/EntityEditViewFormActions.tsx +199 -0
- package/src/core/EntitySidePanel.tsx +85 -15
- package/src/core/FireCMS.tsx +7 -4
- package/src/core/FireCMSRouter.tsx +17 -0
- package/src/core/NavigationRoutes.tsx +23 -32
- package/src/core/SideDialogs.tsx +20 -11
- package/src/core/index.tsx +4 -2
- package/src/form/EntityForm.tsx +774 -0
- package/src/form/EntityFormActions.tsx +169 -0
- package/src/form/PropertyFieldBinding.tsx +13 -11
- package/src/form/components/FormEntry.tsx +22 -0
- package/src/form/components/FormLayout.tsx +16 -0
- package/src/form/components/LabelWithIcon.tsx +6 -3
- package/src/form/components/StorageUploadProgress.tsx +1 -1
- package/src/form/components/index.tsx +2 -0
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +4 -4
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -2
- package/src/form/field_bindings/BlockFieldBinding.tsx +4 -2
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -0
- package/src/form/field_bindings/MapFieldBinding.tsx +14 -16
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +9 -6
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -2
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -1
- package/src/form/field_bindings/RepeatFieldBinding.tsx +5 -2
- package/src/form/field_bindings/SelectFieldBinding.tsx +1 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +6 -4
- package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -0
- package/src/form/field_bindings/TextFieldBinding.tsx +1 -0
- package/src/form/index.tsx +16 -32
- package/src/form/validation.ts +12 -6
- package/src/hooks/data/save.ts +1 -1
- package/src/hooks/data/useCollectionFetch.tsx +1 -1
- package/src/hooks/data/useEntityFetch.tsx +5 -1
- package/src/hooks/useBreadcrumbsController.tsx +31 -0
- package/src/hooks/useBuildModeController.tsx +15 -28
- package/src/hooks/useBuildNavigationController.tsx +70 -24
- package/src/hooks/useLargeLayout.tsx +0 -35
- package/src/hooks/useModeController.tsx +1 -2
- package/src/hooks/useResolvedNavigationFrom.tsx +4 -6
- package/src/internal/useBuildSideDialogsController.tsx +3 -2
- package/src/internal/useBuildSideEntityController.tsx +124 -83
- package/src/internal/useUnsavedChangesDialog.tsx +126 -92
- package/src/preview/PropertyPreview.tsx +8 -8
- package/src/preview/components/UrlComponentPreview.tsx +17 -18
- package/src/preview/property_previews/MapPropertyPreview.tsx +3 -2
- package/src/routes/CustomCMSRoute.tsx +21 -0
- package/src/routes/FireCMSRoute.tsx +235 -0
- package/src/routes/HomePageRoute.tsx +17 -0
- package/src/types/collections.ts +38 -13
- package/src/types/datasource.ts +2 -1
- package/src/types/dialogs_controller.tsx +7 -3
- package/src/types/entities.ts +1 -1
- package/src/types/entity_actions.tsx +12 -0
- package/src/types/fields.tsx +13 -2
- package/src/types/navigation.ts +36 -16
- package/src/types/plugins.tsx +1 -0
- package/src/types/properties.ts +21 -3
- package/src/types/side_dialogs_controller.tsx +2 -0
- package/src/types/side_entity_controller.tsx +6 -1
- package/src/util/builders.ts +5 -3
- package/src/util/entity_actions.ts +28 -0
- package/src/util/entity_cache.ts +204 -0
- package/src/util/index.ts +1 -0
- package/src/util/join_collections.ts +3 -0
- package/src/util/navigation_from_path.ts +13 -6
- package/src/util/navigation_utils.ts +53 -1
- package/src/util/objects.ts +4 -1
- package/src/util/parent_references_from_path.ts +3 -3
- package/src/util/resolutions.ts +26 -5
- package/src/util/useStorageUploadController.tsx +39 -21
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -236
- /package/src/util/{common.tsx → common.ts} +0 -0
|
@@ -2,6 +2,7 @@ import { EntityCollection } from "./collections";
|
|
|
2
2
|
import { ResolvedEntityCollection } from "./resolved_entities";
|
|
3
3
|
import { Entity } from "./entities";
|
|
4
4
|
import { EntityOverrides } from "./entity_overrides";
|
|
5
|
+
import { EntityFormProps } from "../form";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Props used to open a side dialog
|
|
@@ -28,7 +29,7 @@ export interface EntitySidePanelProps<M extends Record<string, any> = any> {
|
|
|
28
29
|
* Open the entity with a selected sub-collection view. If the panel for this
|
|
29
30
|
* entity was already open, it is replaced.
|
|
30
31
|
*/
|
|
31
|
-
|
|
32
|
+
selectedTab?: string;
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Use this prop to override the width of the form view.
|
|
@@ -66,6 +67,10 @@ export interface EntitySidePanelProps<M extends Record<string, any> = any> {
|
|
|
66
67
|
*/
|
|
67
68
|
closeOnSave?: boolean;
|
|
68
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Override some form properties
|
|
72
|
+
*/
|
|
73
|
+
formProps?: Partial<EntityFormProps<M>>;
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
/**
|
package/src/util/builders.ts
CHANGED
|
@@ -26,8 +26,10 @@ import {
|
|
|
26
26
|
* @param collection
|
|
27
27
|
* @group Builder
|
|
28
28
|
*/
|
|
29
|
-
export function buildCollection<
|
|
30
|
-
|
|
29
|
+
export function buildCollection<
|
|
30
|
+
M extends Record<string, any> = any,
|
|
31
|
+
USER extends User = User>
|
|
32
|
+
(
|
|
31
33
|
collection: EntityCollection<M, USER>
|
|
32
34
|
): EntityCollection<M, USER> {
|
|
33
35
|
return collection;
|
|
@@ -39,7 +41,7 @@ export function buildCollection<M extends Record<string, any> = any,
|
|
|
39
41
|
* @param property
|
|
40
42
|
* @group Builder
|
|
41
43
|
*/
|
|
42
|
-
export function buildProperty<T extends CMSType = CMSType, P extends PropertyOrBuilder<T> = PropertyOrBuilder<T>, M extends Record<string, any> =
|
|
44
|
+
export function buildProperty<T extends CMSType = CMSType, P extends PropertyOrBuilder<T> = PropertyOrBuilder<T>, M extends Record<string, any> = any>(
|
|
43
45
|
property: P
|
|
44
46
|
):
|
|
45
47
|
P extends StringProperty ? StringProperty :
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { EntityAction } from "../types";
|
|
2
|
+
|
|
3
|
+
const reservedKeys = ["edit", "copy", "delete"];
|
|
4
|
+
|
|
5
|
+
export function mergeEntityActions(currentActions: EntityAction[], newActions: EntityAction[]): EntityAction[] {
|
|
6
|
+
// given the current actions, replace the ones with the same key
|
|
7
|
+
// and append the new ones
|
|
8
|
+
const updatedActions: EntityAction[] = [];
|
|
9
|
+
currentActions.forEach(action => {
|
|
10
|
+
const newAction = newActions.find(a => a.key === action.key);
|
|
11
|
+
if (newAction) {
|
|
12
|
+
const mergedAction = {
|
|
13
|
+
...action,
|
|
14
|
+
...newAction
|
|
15
|
+
}
|
|
16
|
+
updatedActions.push(mergedAction);
|
|
17
|
+
} else {
|
|
18
|
+
updatedActions.push(action);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
newActions.forEach(action => {
|
|
22
|
+
if (!currentActions.find(a => a.key === action.key) && (!action.key || !reservedKeys.includes(action.key))) {
|
|
23
|
+
updatedActions.push(action);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return updatedActions;
|
|
27
|
+
|
|
28
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { EntityReference, GeoPoint, Vector } from "../types";
|
|
2
|
+
|
|
3
|
+
// Define a unique prefix for entity keys in localStorage to avoid key collisions
|
|
4
|
+
const LOCAL_STORAGE_PREFIX = "entity_cache::";
|
|
5
|
+
|
|
6
|
+
// In-memory cache to store entities for quick access
|
|
7
|
+
const entityCache: Map<string, object> = new Map();
|
|
8
|
+
|
|
9
|
+
// Check `localStorage` availability once during initialization
|
|
10
|
+
const isLocalStorageAvailable = typeof localStorage !== "undefined";
|
|
11
|
+
|
|
12
|
+
// Define custom replacer for JSON.stringify
|
|
13
|
+
function customReplacer(key: string): any {
|
|
14
|
+
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
const value = this[key];
|
|
17
|
+
|
|
18
|
+
// Handle Date objects
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
if (value instanceof Date) {
|
|
21
|
+
return { __type: "Date", value: value.toISOString() };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Handle EntityReference
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
if (value instanceof EntityReference) {
|
|
27
|
+
return { __type: "EntityReference", id: value.id, path: value.path };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Handle GeoPoint
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
if (value instanceof GeoPoint) {
|
|
33
|
+
return { __type: "GeoPoint", latitude: value.latitude, longitude: value.longitude };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Handle Vector
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
if (value instanceof Vector) {
|
|
39
|
+
return { __type: "Vector", value: value.value };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Define custom reviver for JSON.parse
|
|
46
|
+
function customReviver(key: string, value: any): any {
|
|
47
|
+
if (value && typeof value === "object" && "__type" in value) {
|
|
48
|
+
switch (value.__type) {
|
|
49
|
+
case "Date":
|
|
50
|
+
return new Date(value.value);
|
|
51
|
+
case "EntityReference":
|
|
52
|
+
return new EntityReference(value.id, value.path);
|
|
53
|
+
case "GeoPoint":
|
|
54
|
+
return new GeoPoint(value.latitude, value.longitude);
|
|
55
|
+
case "Vector":
|
|
56
|
+
return new Vector(value.value);
|
|
57
|
+
default:
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Initialize the in-memory cache by loading entities from `localStorage`
|
|
65
|
+
if (isLocalStorageAvailable) {
|
|
66
|
+
try {
|
|
67
|
+
// Iterate over all keys in localStorage to find those with the specified prefix
|
|
68
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
69
|
+
const fullKey = localStorage.key(i);
|
|
70
|
+
if (fullKey && fullKey.startsWith(LOCAL_STORAGE_PREFIX)) {
|
|
71
|
+
const path = fullKey.substring(LOCAL_STORAGE_PREFIX.length);
|
|
72
|
+
const entityString = localStorage.getItem(fullKey);
|
|
73
|
+
if (entityString) {
|
|
74
|
+
try {
|
|
75
|
+
const entity: object = JSON.parse(entityString, customReviver);
|
|
76
|
+
entityCache.set(path, entity);
|
|
77
|
+
} catch (parseError) {
|
|
78
|
+
console.error(
|
|
79
|
+
`Failed to parse entity for path "${path}" from localStorage:`,
|
|
80
|
+
parseError
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error("Error accessing localStorage during initialization:", error);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Saves data to the in-memory cache and persists it individually in `localStorage`.
|
|
93
|
+
* @param path - The unique path/key for the data.
|
|
94
|
+
* @param data - The data to cache and persist.
|
|
95
|
+
*/
|
|
96
|
+
export function saveEntityToCache(path: string, data: object): void {
|
|
97
|
+
// Update the in-memory cache
|
|
98
|
+
entityCache.set(path, data);
|
|
99
|
+
|
|
100
|
+
// Persist the data individually in localStorage
|
|
101
|
+
if (isLocalStorageAvailable) {
|
|
102
|
+
try {
|
|
103
|
+
const key = LOCAL_STORAGE_PREFIX + path;
|
|
104
|
+
const entityString = JSON.stringify(data, customReplacer);
|
|
105
|
+
localStorage.setItem(key, entityString);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error(
|
|
108
|
+
`Failed to save entity for path "${path}" to localStorage:`,
|
|
109
|
+
error
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Retrieves an entity from the in-memory cache or `localStorage`.
|
|
117
|
+
* If the entity is not in the cache but exists in `localStorage`, it loads it into the cache.
|
|
118
|
+
* @param path - The unique path/key for the entity.
|
|
119
|
+
* @returns The cached entity or `undefined` if not found.
|
|
120
|
+
*/
|
|
121
|
+
export function getEntityFromCache(path: string): object | undefined {
|
|
122
|
+
|
|
123
|
+
// Attempt to retrieve the entity from the in-memory cache
|
|
124
|
+
if (entityCache.has(path)) {
|
|
125
|
+
return entityCache.get(path);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// If not in the cache, attempt to load it from localStorage
|
|
129
|
+
if (isLocalStorageAvailable) {
|
|
130
|
+
try {
|
|
131
|
+
const key = LOCAL_STORAGE_PREFIX + path;
|
|
132
|
+
const entityString = localStorage.getItem(key);
|
|
133
|
+
if (entityString) {
|
|
134
|
+
const entity: object = JSON.parse(entityString, customReviver);
|
|
135
|
+
entityCache.set(path, entity); // Update the cache
|
|
136
|
+
return entity;
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error(
|
|
140
|
+
`Failed to load entity for path "${path}" from localStorage:`,
|
|
141
|
+
error
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Entity not found
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function hasEntityInCache(path: string): boolean {
|
|
151
|
+
return entityCache.has(path);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Removes an entity from both the in-memory cache and `localStorage`.
|
|
156
|
+
* @param path - The unique path/key for the entity to remove.
|
|
157
|
+
*/
|
|
158
|
+
export function removeEntityFromCache(path: string): void {
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
console.debug("Removing entity from cache", path);
|
|
162
|
+
|
|
163
|
+
// Remove from the in-memory cache
|
|
164
|
+
entityCache.delete(path);
|
|
165
|
+
|
|
166
|
+
// Remove from localStorage
|
|
167
|
+
if (isLocalStorageAvailable) {
|
|
168
|
+
try {
|
|
169
|
+
const key = LOCAL_STORAGE_PREFIX + path;
|
|
170
|
+
localStorage.removeItem(key);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error(
|
|
173
|
+
`Failed to remove entity for path "${path}" from localStorage:`,
|
|
174
|
+
error
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Clears the entire in-memory cache and removes all related entities from `localStorage`.
|
|
182
|
+
*/
|
|
183
|
+
export function clearEntityCache(): void {
|
|
184
|
+
// Clear the in-memory cache
|
|
185
|
+
entityCache.clear();
|
|
186
|
+
|
|
187
|
+
// Remove all entities with the specified prefix from localStorage
|
|
188
|
+
if (isLocalStorageAvailable) {
|
|
189
|
+
try {
|
|
190
|
+
const keysToRemove: string[] = [];
|
|
191
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
192
|
+
const fullKey = localStorage.key(i);
|
|
193
|
+
if (fullKey && fullKey.startsWith(LOCAL_STORAGE_PREFIX)) {
|
|
194
|
+
keysToRemove.push(fullKey);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Remove the keys after collecting them to avoid issues while iterating
|
|
199
|
+
keysToRemove.forEach((key) => localStorage.removeItem(key));
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error("Failed to clear entity cache from localStorage:", error);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
package/src/util/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./objects";
|
|
|
7
7
|
export * from "./paths";
|
|
8
8
|
export * from "./regexp";
|
|
9
9
|
export * from "./navigation_utils";
|
|
10
|
+
export * from "./entity_actions";
|
|
10
11
|
export * from "./useDebouncedCallback";
|
|
11
12
|
export * from "./property_utils";
|
|
12
13
|
export * from "./resolutions";
|
|
@@ -11,22 +11,27 @@ export interface NavigationViewEntityInternal<M extends Record<string, any>> {
|
|
|
11
11
|
type: "entity";
|
|
12
12
|
entityId: string;
|
|
13
13
|
path: string;
|
|
14
|
+
fullPath: string;
|
|
14
15
|
parentCollection: EntityCollection<M>;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export interface NavigationViewCollectionInternal<M extends Record<string, any>> {
|
|
18
19
|
type: "collection";
|
|
20
|
+
id: string;
|
|
19
21
|
path: string;
|
|
22
|
+
fullPath: string;
|
|
20
23
|
collection: EntityCollection<M>;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
export interface NavigationViewEntityCustomInternal<M extends Record<string, any>> {
|
|
24
27
|
type: "custom_view";
|
|
25
28
|
path: string;
|
|
29
|
+
fullPath: string;
|
|
30
|
+
entityId: string;
|
|
26
31
|
view: EntityCustomView<M>;
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
export function
|
|
34
|
+
export function getNavigationEntriesFromPath(props: {
|
|
30
35
|
path: string,
|
|
31
36
|
collections: EntityCollection[] | undefined,
|
|
32
37
|
currentFullPath?: string,
|
|
@@ -56,7 +61,9 @@ export function getNavigationEntriesFromPathInternal(props: {
|
|
|
56
61
|
|
|
57
62
|
result.push({
|
|
58
63
|
type: "collection",
|
|
64
|
+
id: collection.id,
|
|
59
65
|
path: collectionPath,
|
|
66
|
+
fullPath: collectionPath,
|
|
60
67
|
collection
|
|
61
68
|
});
|
|
62
69
|
const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
|
|
@@ -68,6 +75,7 @@ export function getNavigationEntriesFromPathInternal(props: {
|
|
|
68
75
|
type: "entity",
|
|
69
76
|
entityId,
|
|
70
77
|
path: collectionPath,
|
|
78
|
+
fullPath: fullPath,
|
|
71
79
|
parentCollection: collection
|
|
72
80
|
});
|
|
73
81
|
if (nextSegments.length > 1) {
|
|
@@ -81,16 +89,15 @@ export function getNavigationEntriesFromPathInternal(props: {
|
|
|
81
89
|
.filter(Boolean)
|
|
82
90
|
.find((entry) => entry!.key === newPath);
|
|
83
91
|
if (customView) {
|
|
84
|
-
const path = currentFullPath && currentFullPath.length > 0
|
|
85
|
-
? (currentFullPath + "/" + customView.key)
|
|
86
|
-
: customView.key;
|
|
87
92
|
result.push({
|
|
88
93
|
type: "custom_view",
|
|
89
|
-
path,
|
|
94
|
+
path: collectionPath,
|
|
95
|
+
entityId: entityId,
|
|
96
|
+
fullPath: fullPath + "/" + customView.key,
|
|
90
97
|
view: customView
|
|
91
98
|
});
|
|
92
99
|
} else if (collection.subcollections) {
|
|
93
|
-
result.push(...
|
|
100
|
+
result.push(...getNavigationEntriesFromPath({
|
|
94
101
|
path: newPath,
|
|
95
102
|
collections: collection.subcollections,
|
|
96
103
|
currentFullPath: fullPath,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityCollection } from "../types";
|
|
1
|
+
import { EntityCollection, NavigationController, SideEntityController } from "../types";
|
|
2
2
|
|
|
3
3
|
export function removeInitialAndTrailingSlashes(s: string): string {
|
|
4
4
|
return removeInitialSlash(removeTrailingSlash(s));
|
|
@@ -109,3 +109,55 @@ export function getCollectionPathsCombinations(subpaths: string[]): string[] {
|
|
|
109
109
|
return result;
|
|
110
110
|
|
|
111
111
|
}
|
|
112
|
+
|
|
113
|
+
export function navigateToEntity({
|
|
114
|
+
openEntityMode,
|
|
115
|
+
collection,
|
|
116
|
+
entityId,
|
|
117
|
+
copy,
|
|
118
|
+
path,
|
|
119
|
+
selectedTab,
|
|
120
|
+
sideEntityController,
|
|
121
|
+
onClose,
|
|
122
|
+
navigation
|
|
123
|
+
}:
|
|
124
|
+
|
|
125
|
+
{
|
|
126
|
+
openEntityMode: "side_panel" | "full_screen";
|
|
127
|
+
collection?: EntityCollection;
|
|
128
|
+
entityId?: string;
|
|
129
|
+
selectedTab?: string;
|
|
130
|
+
copy?: boolean;
|
|
131
|
+
path: string;
|
|
132
|
+
sideEntityController: SideEntityController;
|
|
133
|
+
onClose?: () => void;
|
|
134
|
+
navigation: NavigationController
|
|
135
|
+
}) {
|
|
136
|
+
|
|
137
|
+
if (openEntityMode === "side_panel") {
|
|
138
|
+
|
|
139
|
+
sideEntityController.open({
|
|
140
|
+
entityId,
|
|
141
|
+
path,
|
|
142
|
+
copy,
|
|
143
|
+
selectedTab,
|
|
144
|
+
collection,
|
|
145
|
+
updateUrl: true,
|
|
146
|
+
onClose
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
} else {
|
|
150
|
+
let to = navigation.buildUrlCollectionPath(entityId ? `${path}/${entityId}` : path);
|
|
151
|
+
if (entityId && selectedTab) {
|
|
152
|
+
to += `/${selectedTab}`;
|
|
153
|
+
}
|
|
154
|
+
if (!entityId) {
|
|
155
|
+
to += "#new";
|
|
156
|
+
}
|
|
157
|
+
if (copy) {
|
|
158
|
+
to += "#copy";
|
|
159
|
+
}
|
|
160
|
+
navigation.navigate(to);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
}
|
package/src/util/objects.ts
CHANGED
|
@@ -18,7 +18,10 @@ export function mergeDeep<T extends Record<any, any>, U extends Record<any, any>
|
|
|
18
18
|
if (targetIsObject && isObject(source)) {
|
|
19
19
|
Object.keys(source).forEach(key => {
|
|
20
20
|
const sourceElement = source[key];
|
|
21
|
-
if (
|
|
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)) {
|
|
22
25
|
if (!(key in target))
|
|
23
26
|
Object.assign(output, { [key]: sourceElement });
|
|
24
27
|
else
|
|
@@ -22,11 +22,11 @@ export function getParentReferencesFromPath(props: {
|
|
|
22
22
|
|
|
23
23
|
const collection: EntityCollection<any> | undefined = collections && collections.find((entry) => entry.id === subpathCombination || entry.path === subpathCombination);
|
|
24
24
|
|
|
25
|
+
// If we find a collection, we add the reference and continue
|
|
25
26
|
if (collection) {
|
|
26
|
-
const pathOrAlias = collection.id ?? collection.path;
|
|
27
27
|
const collectionPath = currentFullPath && currentFullPath.length > 0
|
|
28
|
-
? (currentFullPath + "/" +
|
|
29
|
-
:
|
|
28
|
+
? (currentFullPath + "/" + collection.path)
|
|
29
|
+
: collection.path;
|
|
30
30
|
|
|
31
31
|
const restOfThePath = removeInitialAndTrailingSlashes(removeInitialAndTrailingSlashes(path).replace(subpathCombination, ""));
|
|
32
32
|
const nextSegments = restOfThePath.length > 0 ? restOfThePath.split("/") : [];
|
package/src/util/resolutions.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ArrayProperty,
|
|
3
3
|
CMSType,
|
|
4
|
+
CustomizationController,
|
|
4
5
|
EntityCollection,
|
|
5
6
|
EntityCustomView,
|
|
6
7
|
EntityValues,
|
|
@@ -218,11 +219,11 @@ export function resolveProperty<T extends CMSType = CMSType, M extends Record<st
|
|
|
218
219
|
}
|
|
219
220
|
|
|
220
221
|
export function getArrayResolvedProperties<M>({
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
propertyKey,
|
|
223
|
+
propertyValue,
|
|
224
|
+
property,
|
|
225
|
+
...props
|
|
226
|
+
}: {
|
|
226
227
|
propertyValue: any,
|
|
227
228
|
propertyKey?: string,
|
|
228
229
|
property: ArrayProperty<any> | ResolvedArrayProperty<any>,
|
|
@@ -430,3 +431,23 @@ export function resolveEntityView(entityView: string | EntityCustomView<any>, co
|
|
|
430
431
|
return entityView;
|
|
431
432
|
}
|
|
432
433
|
}
|
|
434
|
+
|
|
435
|
+
export function resolvedSelectedEntityView<M extends Record<string, any>>(
|
|
436
|
+
customViews: (string | EntityCustomView<M>)[] | undefined,
|
|
437
|
+
customizationController: CustomizationController,
|
|
438
|
+
selectedTab?: string
|
|
439
|
+
) {
|
|
440
|
+
const resolvedEntityViews = customViews ? customViews
|
|
441
|
+
.map(e => resolveEntityView(e, customizationController.entityViews))
|
|
442
|
+
.filter(Boolean) as EntityCustomView[]
|
|
443
|
+
: [];
|
|
444
|
+
|
|
445
|
+
const selectedEntityView = resolvedEntityViews.find(e => e.key === selectedTab);
|
|
446
|
+
const selectedSecondaryForm = customViews
|
|
447
|
+
&& resolvedEntityViews.filter(e => e.includeActions).find(e => e.key === selectedTab);
|
|
448
|
+
return {
|
|
449
|
+
resolvedEntityViews,
|
|
450
|
+
selectedEntityView,
|
|
451
|
+
selectedSecondaryForm
|
|
452
|
+
};
|
|
453
|
+
}
|
|
@@ -34,6 +34,7 @@ export interface StorageFieldItem {
|
|
|
34
34
|
size: PreviewSize
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
|
|
37
38
|
export function useStorageUploadController<M extends object>({
|
|
38
39
|
entityId,
|
|
39
40
|
entityValues,
|
|
@@ -45,17 +46,17 @@ export function useStorageUploadController<M extends object>({
|
|
|
45
46
|
disabled,
|
|
46
47
|
onChange
|
|
47
48
|
}:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
{
|
|
50
|
+
entityId: string,
|
|
51
|
+
entityValues: EntityValues<M>,
|
|
52
|
+
value: string | string[] | null;
|
|
53
|
+
path?: string,
|
|
54
|
+
propertyKey: string,
|
|
55
|
+
property: StringProperty | ArrayProperty<string[]> | ResolvedStringProperty | ResolvedArrayProperty<string[]>,
|
|
56
|
+
storageSource: StorageSource,
|
|
57
|
+
disabled: boolean,
|
|
58
|
+
onChange: (value: string | string[] | null) => void
|
|
59
|
+
}) {
|
|
59
60
|
|
|
60
61
|
const storage: StorageConfig | undefined = property.dataType === "string"
|
|
61
62
|
? property.storage
|
|
@@ -75,16 +76,7 @@ export function useStorageUploadController<M extends object>({
|
|
|
75
76
|
const compression: ImageCompression | undefined = storage?.imageCompression;
|
|
76
77
|
|
|
77
78
|
const internalInitialValue: StorageFieldItem[] =
|
|
78
|
-
(multipleFilesSupported
|
|
79
|
-
? (value ?? []) as string[]
|
|
80
|
-
: value ? [value as string] : []).map(entry => (
|
|
81
|
-
{
|
|
82
|
-
id: getRandomId(),
|
|
83
|
-
storagePathOrDownloadUrl: entry,
|
|
84
|
-
metadata,
|
|
85
|
-
size
|
|
86
|
-
}
|
|
87
|
-
));
|
|
79
|
+
getInternalInitialValue(multipleFilesSupported, value, metadata, size);
|
|
88
80
|
|
|
89
81
|
const [initialValue, setInitialValue] = useState<string | string[] | null>(value);
|
|
90
82
|
const [internalValue, setInternalValue] = useState<StorageFieldItem[]>(internalInitialValue);
|
|
@@ -226,6 +218,32 @@ export function useStorageUploadController<M extends object>({
|
|
|
226
218
|
}
|
|
227
219
|
}
|
|
228
220
|
|
|
221
|
+
function getInternalInitialValue(multipleFilesSupported: boolean,
|
|
222
|
+
value: string | string[] | null,
|
|
223
|
+
metadata: Record<string, any> | undefined,
|
|
224
|
+
size: PreviewSize): StorageFieldItem[] {
|
|
225
|
+
let strings: string[] = [];
|
|
226
|
+
if (multipleFilesSupported) {
|
|
227
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
|
|
228
|
+
strings = (value ?? []) as string[];
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
if (typeof value === "string") {
|
|
232
|
+
strings = value ? [value as string] : [];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return strings
|
|
237
|
+
.map(entry => (
|
|
238
|
+
{
|
|
239
|
+
id: getRandomId(),
|
|
240
|
+
storagePathOrDownloadUrl: entry,
|
|
241
|
+
metadata,
|
|
242
|
+
size
|
|
243
|
+
}
|
|
244
|
+
));
|
|
245
|
+
}
|
|
246
|
+
|
|
229
247
|
function removeDuplicates(items: StorageFieldItem[]) {
|
|
230
248
|
return items.filter(
|
|
231
249
|
(item, i) => {
|