@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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firecms/core",
3
3
  "type": "module",
4
- "version": "3.0.0-canary.182",
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.182",
54
- "@firecms/formex": "^3.0.0-canary.182",
55
- "@firecms/ui": "^3.0.0-canary.182",
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": "1b50d62a2f83c8d913f2bfbf8245c89c147e74e6",
107
+ "gitHead": "ece7ace38466826a58d560fcebd154e74ecedd6b",
108
108
  "publishConfig": {
109
109
  "access": "public"
110
110
  },
@@ -90,11 +90,11 @@ export function mapPropertyToYup<T extends CMSType>(propertyContext: PropertyCon
90
90
  }
91
91
 
92
92
  export function getYupMapObjectSchema({
93
- property,
94
- entityId,
95
- customFieldValidator,
96
- name
97
- }: PropertyContext<Record<string, any>>): ObjectSchema<any> {
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) collection = collection.url(`${property.name} must be a 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.url) {
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
- {!(childProperty.dataType === "map" || childProperty === "array") &&
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
- {(childProperty.dataType === "map" || childProperty === "array") &&
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
- updateUrl(params.entityId, params.selectedTab, true, params.path, isNew);
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
  />
@@ -150,7 +150,7 @@ export function hasEntityInCache(path: string): boolean {
150
150
  export function removeEntityFromCache(path: string): void {
151
151
 
152
152
 
153
- console.log("Removing entity from cache", path);
153
+ console.debug("Removing entity from cache", path);
154
154
 
155
155
  // Remove from the in-memory cache
156
156
  entityCache.delete(path);