@firecms/core 3.0.0-canary.182 → 3.0.0-canary.184
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/hooks/useBuildNavigationController.d.ts +4 -0
- package/dist/index.es.js +166 -160
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +166 -160
- package/dist/index.umd.js.map +1 -1
- package/package.json +5 -5
- package/src/form/validation.ts +12 -6
- package/src/hooks/useBuildNavigationController.tsx +9 -2
- package/src/preview/PropertyPreview.tsx +8 -8
- package/src/preview/property_previews/MapPropertyPreview.tsx +3 -2
- package/src/routes/FireCMSRoute.tsx +21 -33
- package/src/util/entity_cache.ts +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.0-canary.
|
|
4
|
+
"version": "3.0.0-canary.184",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"./package.json": "./package.json"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@firecms/editor": "^3.0.0-canary.
|
|
54
|
-
"@firecms/formex": "^3.0.0-canary.
|
|
55
|
-
"@firecms/ui": "^3.0.0-canary.
|
|
53
|
+
"@firecms/editor": "^3.0.0-canary.184",
|
|
54
|
+
"@firecms/formex": "^3.0.0-canary.184",
|
|
55
|
+
"@firecms/ui": "^3.0.0-canary.184",
|
|
56
56
|
"@hello-pangea/dnd": "^17.0.0",
|
|
57
57
|
"@radix-ui/react-portal": "^1.1.3",
|
|
58
58
|
"clsx": "^2.1.1",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"dist",
|
|
105
105
|
"src"
|
|
106
106
|
],
|
|
107
|
-
"gitHead": "
|
|
107
|
+
"gitHead": "ece7ace38466826a58d560fcebd154e74ecedd6b",
|
|
108
108
|
"publishConfig": {
|
|
109
109
|
"access": "public"
|
|
110
110
|
},
|
package/src/form/validation.ts
CHANGED
|
@@ -90,11 +90,11 @@ export function mapPropertyToYup<T extends CMSType>(propertyContext: PropertyCon
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
export function getYupMapObjectSchema({
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
property,
|
|
94
|
+
entityId,
|
|
95
|
+
customFieldValidator,
|
|
96
|
+
name
|
|
97
|
+
}: PropertyContext<Record<string, any>>): ObjectSchema<any> {
|
|
98
98
|
const objectSchema: any = {};
|
|
99
99
|
const validation = property.validation;
|
|
100
100
|
if (property.properties)
|
|
@@ -159,7 +159,13 @@ function getYupStringSchema({
|
|
|
159
159
|
if (validation.lowercase) collection = collection.lowercase();
|
|
160
160
|
if (validation.uppercase) collection = collection.uppercase();
|
|
161
161
|
if (property.email) collection = collection.email(`${property.name} must be an email`);
|
|
162
|
-
if (property.url)
|
|
162
|
+
if (property.url) {
|
|
163
|
+
if (!property.storage || property.storage?.storeUrl) {
|
|
164
|
+
collection = collection.url(`${property.name} must be a url`);
|
|
165
|
+
} else {
|
|
166
|
+
console.warn(`Property ${property.name} has a url validation but its storage configuration is not set to store urls`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
163
169
|
} else {
|
|
164
170
|
collection = collection.notRequired().nullable(true);
|
|
165
171
|
}
|
|
@@ -51,6 +51,11 @@ export type BuildNavigationContextProps<EC extends EntityCollection, USER extend
|
|
|
51
51
|
* @param collections
|
|
52
52
|
*/
|
|
53
53
|
injectCollections?: (collections: EntityCollection[]) => EntityCollection[];
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* If true, the navigation logic will not be updated until this flag is false
|
|
57
|
+
*/
|
|
58
|
+
disabled?: boolean;
|
|
54
59
|
};
|
|
55
60
|
|
|
56
61
|
export function useBuildNavigationController<EC extends EntityCollection, USER extends User>(props: BuildNavigationContextProps<EC, USER>): NavigationController {
|
|
@@ -65,7 +70,8 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
|
|
|
65
70
|
viewsOrder,
|
|
66
71
|
userConfigPersistence,
|
|
67
72
|
dataSourceDelegate,
|
|
68
|
-
injectCollections
|
|
73
|
+
injectCollections,
|
|
74
|
+
disabled
|
|
69
75
|
} = props;
|
|
70
76
|
|
|
71
77
|
const navigate = useNavigate();
|
|
@@ -189,7 +195,7 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
|
|
|
189
195
|
|
|
190
196
|
const refreshNavigation = useCallback(async () => {
|
|
191
197
|
|
|
192
|
-
if (authController.initialLoading)
|
|
198
|
+
if (disabled || authController.initialLoading)
|
|
193
199
|
return;
|
|
194
200
|
|
|
195
201
|
console.debug("Refreshing navigation");
|
|
@@ -241,6 +247,7 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
|
|
|
241
247
|
collectionPermissions,
|
|
242
248
|
authController.user,
|
|
243
249
|
authController.initialLoading,
|
|
250
|
+
disabled,
|
|
244
251
|
viewsProp,
|
|
245
252
|
adminViewsProp,
|
|
246
253
|
computeTopNavigation,
|
|
@@ -75,7 +75,14 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
75
75
|
} else if (property.dataType === "string") {
|
|
76
76
|
const stringProperty = property as ResolvedStringProperty;
|
|
77
77
|
if (typeof value === "string") {
|
|
78
|
-
if (stringProperty.
|
|
78
|
+
if (stringProperty.storage) {
|
|
79
|
+
const filePath = stringProperty.storage.previewUrl ? stringProperty.storage.previewUrl(value) : value;
|
|
80
|
+
content = <StorageThumbnail
|
|
81
|
+
interactive={interactive}
|
|
82
|
+
storeUrl={property.storage?.storeUrl ?? false}
|
|
83
|
+
size={props.size}
|
|
84
|
+
storagePathOrDownloadUrl={filePath}/>;
|
|
85
|
+
} else if (stringProperty.url) {
|
|
79
86
|
if (typeof stringProperty.url === "boolean")
|
|
80
87
|
content =
|
|
81
88
|
<UrlComponentPreview size={props.size}
|
|
@@ -88,13 +95,6 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
|
|
|
88
95
|
previewType={stringProperty.url}/>;
|
|
89
96
|
} else if (stringProperty.markdown) {
|
|
90
97
|
content = <Markdown source={value} size={"small"}/>;
|
|
91
|
-
} else if (stringProperty.storage) {
|
|
92
|
-
const filePath = stringProperty.storage.previewUrl ? stringProperty.storage.previewUrl(value) : value;
|
|
93
|
-
content = <StorageThumbnail
|
|
94
|
-
interactive={interactive}
|
|
95
|
-
storeUrl={property.storage?.storeUrl ?? false}
|
|
96
|
-
size={props.size}
|
|
97
|
-
storagePathOrDownloadUrl={filePath}/>;
|
|
98
98
|
} else {
|
|
99
99
|
content = <StringPropertyPreview {...props}
|
|
100
100
|
property={stringProperty}
|
|
@@ -58,6 +58,7 @@ export function MapPropertyPreview<T extends Record<string, any> = Record<string
|
|
|
58
58
|
{mapPropertyKeys &&
|
|
59
59
|
mapPropertyKeys.map((key, index) => {
|
|
60
60
|
const childProperty = mapProperty.properties![key];
|
|
61
|
+
const isArrayOrMap = childProperty.dataType === "map" || childProperty === "array";
|
|
61
62
|
return (
|
|
62
63
|
<div
|
|
63
64
|
key={`map_preview_table_${key}}`}
|
|
@@ -75,7 +76,7 @@ export function MapPropertyPreview<T extends Record<string, any> = Record<string
|
|
|
75
76
|
<div
|
|
76
77
|
className="flex-grow max-w-[75%]">
|
|
77
78
|
<ErrorBoundary>
|
|
78
|
-
{!
|
|
79
|
+
{!isArrayOrMap &&
|
|
79
80
|
<PropertyPreview
|
|
80
81
|
propertyKey={key}
|
|
81
82
|
value={(value)[key]}
|
|
@@ -86,7 +87,7 @@ export function MapPropertyPreview<T extends Record<string, any> = Record<string
|
|
|
86
87
|
</div>
|
|
87
88
|
</div>
|
|
88
89
|
|
|
89
|
-
{
|
|
90
|
+
{isArrayOrMap &&
|
|
90
91
|
<div className={cls(defaultBorderMixin, "border-l pl-4 ml-2 my-2")}>
|
|
91
92
|
<PropertyPreview
|
|
92
93
|
propertyKey={key}
|
|
@@ -23,6 +23,7 @@ export function FireCMSRoute() {
|
|
|
23
23
|
const hash = location.hash;
|
|
24
24
|
const isSidePanel = hash.includes("#side");
|
|
25
25
|
const isNew = hash.includes("#new") || hash.includes("#new_side");
|
|
26
|
+
const isCopy = hash.includes("#copy");
|
|
26
27
|
|
|
27
28
|
const pathname = location.pathname;
|
|
28
29
|
const navigationPath = navigation.urlPathToDataPath(pathname);
|
|
@@ -62,6 +63,7 @@ export function FireCMSRoute() {
|
|
|
62
63
|
pathname={pathname}
|
|
63
64
|
navigationEntries={navigationEntries}
|
|
64
65
|
isNew={true}
|
|
66
|
+
isCopy={false}
|
|
65
67
|
/>;
|
|
66
68
|
}
|
|
67
69
|
|
|
@@ -100,6 +102,7 @@ export function FireCMSRoute() {
|
|
|
100
102
|
pathname={pathname}
|
|
101
103
|
navigationEntries={navigationEntries}
|
|
102
104
|
isNew={isNew}
|
|
105
|
+
isCopy={isCopy}
|
|
103
106
|
/>;
|
|
104
107
|
|
|
105
108
|
}
|
|
@@ -107,11 +110,13 @@ export function FireCMSRoute() {
|
|
|
107
110
|
function EntityFullScreenRoute({
|
|
108
111
|
pathname,
|
|
109
112
|
navigationEntries,
|
|
110
|
-
isNew
|
|
113
|
+
isNew,
|
|
114
|
+
isCopy
|
|
111
115
|
}: {
|
|
112
116
|
pathname: string;
|
|
113
117
|
navigationEntries: NavigationViewInternal[],
|
|
114
|
-
isNew: boolean
|
|
118
|
+
isNew: boolean,
|
|
119
|
+
isCopy: boolean
|
|
115
120
|
}) {
|
|
116
121
|
|
|
117
122
|
const navigation = useNavigationController();
|
|
@@ -160,34 +165,6 @@ function EntityFullScreenRoute({
|
|
|
160
165
|
console.warn("Blocker not available, navigation will not be blocked");
|
|
161
166
|
}
|
|
162
167
|
|
|
163
|
-
function updateUrl(entityId: string | undefined, newSelectedTab: string | undefined, replace: boolean, path: string, isNew: boolean) {
|
|
164
|
-
|
|
165
|
-
console.log("Updating url", {
|
|
166
|
-
entityId,
|
|
167
|
-
newSelectedTab,
|
|
168
|
-
replace,
|
|
169
|
-
basePath,
|
|
170
|
-
path,
|
|
171
|
-
isNew
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
if (!isNew && (newSelectedTab ?? null) === (selectedTab ?? null)) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (isNew) {
|
|
179
|
-
navigate(`${basePath}/${entityId}`, { replace: replace });
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (newSelectedTab) {
|
|
184
|
-
navigate(`${basePath}/${entityId}/${newSelectedTab}`, { replace: replace });
|
|
185
|
-
} else {
|
|
186
|
-
navigate(`${basePath}/${entityId}`, { replace: replace });
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
168
|
const lastCollectionEntry = navigationEntries.findLast((entry) => entry.type === "collection");
|
|
192
169
|
|
|
193
170
|
if (isNew && !lastCollectionEntry) {
|
|
@@ -203,19 +180,30 @@ function EntityFullScreenRoute({
|
|
|
203
180
|
|
|
204
181
|
return <>
|
|
205
182
|
<EntityEditView
|
|
206
|
-
key={collection.id + "_" + (isNew ? "new" : entityId)}
|
|
183
|
+
key={collection.id + "_" + (isNew ? "new" : (isCopy ? entityId + "_copy" : entityId))}
|
|
207
184
|
entityId={isNew ? undefined : entityId}
|
|
208
185
|
collection={collection}
|
|
209
186
|
layout={"full_screen"}
|
|
210
187
|
path={collectionPath}
|
|
188
|
+
copy={isCopy}
|
|
211
189
|
selectedTab={selectedTab ?? undefined}
|
|
212
190
|
onValuesModified={(modified) => blocked.current = modified}
|
|
213
191
|
onSaved={(params) => {
|
|
214
|
-
|
|
192
|
+
console.log("Entity saved", params);
|
|
193
|
+
navigate(`${basePath}/${params.entityId}`, { replace: true });
|
|
215
194
|
}}
|
|
216
195
|
onTabChange={(params) => {
|
|
217
|
-
updateUrl(params.entityId, params.selectedTab, !isNew, params.path, isNew);
|
|
196
|
+
// updateUrl(params.entityId, params.selectedTab, !isNew, params.path, isNew);
|
|
218
197
|
setSelectedTab(params.selectedTab);
|
|
198
|
+
if (isNew) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const newSelectedTab = params.selectedTab;
|
|
202
|
+
if (newSelectedTab) {
|
|
203
|
+
navigate(`${basePath}/${entityId}/${newSelectedTab}`, { replace: true });
|
|
204
|
+
} else {
|
|
205
|
+
navigate(`${basePath}/${entityId}`, { replace: true });
|
|
206
|
+
}
|
|
219
207
|
}}
|
|
220
208
|
parentCollectionIds={parentCollectionIds}
|
|
221
209
|
/>
|
package/src/util/entity_cache.ts
CHANGED
|
@@ -150,7 +150,7 @@ export function hasEntityInCache(path: string): boolean {
|
|
|
150
150
|
export function removeEntityFromCache(path: string): void {
|
|
151
151
|
|
|
152
152
|
|
|
153
|
-
console.
|
|
153
|
+
console.debug("Removing entity from cache", path);
|
|
154
154
|
|
|
155
155
|
// Remove from the in-memory cache
|
|
156
156
|
entityCache.delete(path);
|