@firecms/core 3.0.0-beta.5 → 3.0.0-beta.6

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 (67) hide show
  1. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  2. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
  3. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +1 -1
  4. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  5. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +11 -1
  6. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  7. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  8. package/dist/components/common/types.d.ts +1 -1
  9. package/dist/form/components/ErrorFocus.d.ts +1 -1
  10. package/dist/hooks/useValidateAuthenticator.d.ts +2 -6
  11. package/dist/index.es.js +3516 -3381
  12. package/dist/index.es.js.map +1 -1
  13. package/dist/index.umd.js +5 -5
  14. package/dist/index.umd.js.map +1 -1
  15. package/dist/internal/useBuildDataSource.d.ts +1 -16
  16. package/dist/types/collections.d.ts +5 -1
  17. package/dist/types/datasource.d.ts +2 -5
  18. package/dist/types/entities.d.ts +5 -1
  19. package/dist/types/index.d.ts +0 -1
  20. package/dist/types/navigation.d.ts +1 -1
  21. package/dist/types/plugins.d.ts +3 -1
  22. package/dist/types/properties.d.ts +2 -2
  23. package/dist/util/entities.d.ts +1 -1
  24. package/dist/util/navigation_utils.d.ts +2 -2
  25. package/dist/util/resolutions.d.ts +5 -5
  26. package/package.json +22 -19
  27. package/src/components/ClearFilterSortButton.tsx +41 -0
  28. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -11
  29. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
  30. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +16 -19
  31. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
  32. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +11 -6
  33. package/src/components/EntityCollectionView/EntityCollectionView.tsx +28 -5
  34. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  35. package/src/components/EntityView.tsx +1 -1
  36. package/src/components/HomePage/DefaultHomePage.tsx +1 -1
  37. package/src/components/SelectableTable/SelectableTable.tsx +1 -1
  38. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  39. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +22 -7
  40. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +24 -5
  41. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  42. package/src/components/VirtualTable/VirtualTable.tsx +28 -20
  43. package/src/components/common/types.tsx +1 -1
  44. package/src/core/FireCMS.tsx +1 -2
  45. package/src/core/field_configs.tsx +1 -2
  46. package/src/hooks/data/save.ts +1 -0
  47. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +9 -10
  48. package/src/hooks/useBuildModeController.tsx +11 -5
  49. package/src/hooks/useBuildNavigationController.tsx +76 -19
  50. package/src/hooks/useProjectLog.tsx +3 -3
  51. package/src/hooks/useValidateAuthenticator.tsx +2 -22
  52. package/src/internal/useBuildDataSource.ts +42 -47
  53. package/src/internal/useBuildSideEntityController.tsx +16 -13
  54. package/src/preview/PropertyPreview.tsx +2 -12
  55. package/src/types/collections.ts +5 -1
  56. package/src/types/datasource.ts +7 -4
  57. package/src/types/entities.ts +9 -1
  58. package/src/types/index.ts +0 -1
  59. package/src/types/navigation.ts +1 -1
  60. package/src/types/plugins.tsx +4 -3
  61. package/src/types/properties.ts +5 -4
  62. package/src/util/entities.ts +1 -1
  63. package/src/util/navigation_utils.ts +6 -6
  64. package/dist/internal/useLocaleConfig.d.ts +0 -1
  65. package/dist/types/appcheck.d.ts +0 -26
  66. package/src/internal/useLocaleConfig.tsx +0 -18
  67. package/src/types/appcheck.ts +0 -29
@@ -1,10 +1,10 @@
1
- import { useCallback, useEffect, useState } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { PartialEntityCollection, UserConfigurationPersistence } from "../types";
3
3
  import { mergeDeep, stripCollectionPath } from "../util";
4
4
 
5
5
  export function useBuildLocalConfigurationPersistence(): UserConfigurationPersistence {
6
6
 
7
- const [configCache, setConfigCache] = useState<Record<string, PartialEntityCollection>>({});
7
+ const configCache = useRef<Record<string, PartialEntityCollection>>({});
8
8
 
9
9
  const getCollectionFromStorage = useCallback((storageKey: string) => {
10
10
  const item = localStorage.getItem(storageKey);
@@ -13,20 +13,19 @@ export function useBuildLocalConfigurationPersistence(): UserConfigurationPersis
13
13
 
14
14
  const getCollectionConfig = useCallback(<M extends Record<string, any>>(path: string): PartialEntityCollection<M> => {
15
15
  const storageKey = `collection_config::${stripCollectionPath(path)}`;
16
- if (configCache[storageKey]) {
17
- return configCache[storageKey] as PartialEntityCollection<M>;
16
+ if (configCache.current[storageKey]) {
17
+ return configCache.current[storageKey] as PartialEntityCollection<M>;
18
18
  }
19
19
  return getCollectionFromStorage(storageKey);
20
- }, [configCache, getCollectionFromStorage]);
20
+ }, [getCollectionFromStorage]);
21
21
 
22
22
  const onCollectionModified = useCallback(<M extends Record<string, any>>(path: string, data: PartialEntityCollection<M>) => {
23
23
  const storageKey = `collection_config::${stripCollectionPath(path)}`;
24
24
  localStorage.setItem(storageKey, JSON.stringify(data));
25
- setConfigCache((currentCache) => {
26
- const cachedConfig = currentCache[storageKey];
27
- const newConfig = mergeDeep(cachedConfig ?? getCollectionFromStorage(path), data);
28
- return (mergeDeep(currentCache, newConfig));
29
- });
25
+ const currentCache = configCache.current;
26
+ const cachedConfig = currentCache[storageKey];
27
+ const newConfig = mergeDeep(cachedConfig ?? getCollectionFromStorage(path), data);
28
+ configCache.current = mergeDeep(currentCache, newConfig);
30
29
  }, [getCollectionFromStorage]);
31
30
 
32
31
  const [recentlyVisitedPaths, _setRecentlyVisitedPaths] = useState<string[]>([]);
@@ -8,8 +8,13 @@ import { ModeController } from "./index";
8
8
  */
9
9
  export function useBuildModeController(): ModeController {
10
10
 
11
- const prefersDarkModeQuery = typeof window !== "undefined" &&
12
- window.matchMedia("(prefers-color-scheme: dark)");
11
+ const prefersDarkModeQuery = useCallback((): boolean => {
12
+ if (typeof window === "undefined")
13
+ return false;
14
+ const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
15
+ return mediaQueryList.matches;
16
+ }, []);
17
+
13
18
  const prefersDarkModeStorage: boolean | null = localStorage.getItem("prefers-dark-mode") != null ? localStorage.getItem("prefers-dark-mode") === "true" : null;
14
19
  const prefersDarkMode = prefersDarkModeStorage ?? prefersDarkModeQuery;
15
20
  const [mode, setMode] = useState<"light" | "dark">(prefersDarkMode ? "dark" : "light");
@@ -23,7 +28,7 @@ export function useBuildModeController(): ModeController {
23
28
  const setDarkMode = useCallback(() => {
24
29
  setMode("dark");
25
30
  setDocumentMode("dark");
26
- }, [prefersDarkModeQuery]);
31
+ }, []);
27
32
 
28
33
  const setLightMode = useCallback(() => {
29
34
  setMode("light");
@@ -37,14 +42,15 @@ export function useBuildModeController(): ModeController {
37
42
 
38
43
  const toggleMode = useCallback(() => {
39
44
 
45
+ const prefersDarkModeQueryResult = prefersDarkModeQuery();
40
46
  if (mode === "light") {
41
- if (!prefersDarkModeQuery)
47
+ if (!prefersDarkModeQueryResult)
42
48
  localStorage.setItem("prefers-dark-mode", "true");
43
49
  else
44
50
  localStorage.removeItem("prefers-dark-mode");
45
51
  setDarkMode();
46
52
  } else {
47
- if (prefersDarkModeQuery)
53
+ if (prefersDarkModeQueryResult)
48
54
  localStorage.setItem("prefers-dark-mode", "false");
49
55
  else
50
56
  localStorage.removeItem("prefers-dark-mode");
@@ -130,6 +130,30 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
130
130
  .filter(Boolean) as TopNavigationEntry[]
131
131
  ];
132
132
 
133
+ // Sort by group, entries with group "Admin" will go last, and second to last will be the group "Views"
134
+ navigationEntries = navigationEntries.sort((a, b) => {
135
+ if (a.group !== "Views" && a.group !== "Admin" && (b.group === "Views" || b.group === "Admin")) {
136
+ return -1;
137
+ }
138
+ if (b.group !== "Views" && b.group !== "Admin" && (a.group === "Views" || a.group === "Admin")) {
139
+ return 1;
140
+ }
141
+ if (a.group === "Admin" && b.group !== "Admin") {
142
+ return 1;
143
+ }
144
+ if (a.group !== "Admin" && b.group === "Admin") {
145
+ return -1;
146
+ }
147
+ if (a.group === "Views" && b.group !== "Views") {
148
+ return -1;
149
+ }
150
+ if (a.group !== "Views" && b.group === "Views") {
151
+ return 1;
152
+ }
153
+ return 0;
154
+
155
+ });
156
+
133
157
  if (viewsOrder) {
134
158
  navigationEntries = navigationEntries.sort((a, b) => {
135
159
  const aIndex = viewsOrder.indexOf(a.path);
@@ -163,6 +187,8 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
163
187
  if (authController.initialLoading)
164
188
  return;
165
189
 
190
+ console.debug("Refreshing navigation");
191
+
166
192
  try {
167
193
 
168
194
  const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([
@@ -172,16 +198,23 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
172
198
  ]
173
199
  );
174
200
 
175
- if (
176
- !equal(collectionsRef.current, resolvedCollections) ||
177
- !equal(viewsRef.current, resolvedViews) ||
178
- !equal(adminViewsRef.current, resolvedAdminViews) ||
179
- !equal(topLevelNavigation, computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder))
180
- ) {
201
+ let shouldUpdateTopLevelNav = false;
202
+ if (!areCollectionListsEqual(collectionsRef.current ?? [], resolvedCollections)) {
181
203
  collectionsRef.current = resolvedCollections;
204
+ shouldUpdateTopLevelNav = true;
205
+ }
206
+ if (!equal(viewsRef.current, resolvedViews)) {
182
207
  viewsRef.current = resolvedViews;
208
+ shouldUpdateTopLevelNav = true;
209
+ }
210
+ if (!equal(adminViewsRef.current, resolvedAdminViews)) {
183
211
  adminViewsRef.current = resolvedAdminViews;
184
- setTopLevelNavigation(computeTopNavigation(resolvedCollections ?? [], resolvedViews, resolvedAdminViews, viewsOrder));
212
+ shouldUpdateTopLevelNav = true;
213
+ }
214
+
215
+ const computedTopLevelNav = computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder);
216
+ if (shouldUpdateTopLevelNav && !equal(topLevelNavigation, computedTopLevelNav)) {
217
+ setTopLevelNavigation(computedTopLevelNav);
185
218
  }
186
219
  } catch (e) {
187
220
  console.error(e);
@@ -243,9 +276,9 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
243
276
  const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
244
277
 
245
278
  const collections = collectionsRef.current;
279
+ if (collections === undefined)
280
+ throw Error("getCollectionFromPaths: Collections have not been initialised yet");
246
281
  let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
247
- if (!currentCollections)
248
- throw Error("Collections have not been initialised yet");
249
282
 
250
283
  for (let i = 0; i < pathSegments.length; i++) {
251
284
  const pathSegment = pathSegments[i];
@@ -264,9 +297,9 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
264
297
  const getCollectionFromIds = useCallback(<EC extends EntityCollection>(ids: string[]): EC | undefined => {
265
298
 
266
299
  const collections = collectionsRef.current;
300
+ if (collections === undefined)
301
+ throw Error("getCollectionFromIds: Collections have not been initialised yet");
267
302
  let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
268
- if (!currentCollections)
269
- throw Error("Collections have not been initialised yet");
270
303
 
271
304
  for (let i = 0; i < ids.length; i++) {
272
305
  const id = ids[i];
@@ -302,9 +335,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
302
335
  []);
303
336
 
304
337
  const resolveAliasesFrom = useCallback((path: string): string => {
305
- const collections = collectionsRef.current;
306
- if (!collections)
307
- throw Error("Collections have not been initialised yet");
338
+ const collections = collectionsRef.current ?? [];
308
339
  return resolveCollectionPathIds(path, collections);
309
340
  }, []);
310
341
 
@@ -407,7 +438,7 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
407
438
  collectionPermissions: PermissionsBuilder | undefined,
408
439
  authController: AuthController,
409
440
  dataSource: DataSourceDelegate,
410
- injectCollections?: (collections: EntityCollection[]) => EntityCollection[]) {
441
+ injectCollections?: (collections: EntityCollection[]) => EntityCollection[]): Promise<EntityCollection[]> {
411
442
  let resolvedCollections: EntityCollection[] = [];
412
443
  if (typeof collections === "function") {
413
444
  resolvedCollections = await collections({
@@ -419,14 +450,14 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
419
450
  resolvedCollections = collections;
420
451
  }
421
452
 
422
- resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
423
-
424
- resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
425
-
426
453
  if (injectCollections) {
427
454
  resolvedCollections = injectCollections(resolvedCollections ?? []);
428
455
  }
429
456
 
457
+ resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
458
+
459
+ resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
460
+
430
461
  return resolvedCollections;
431
462
  }
432
463
 
@@ -451,3 +482,29 @@ function getGroup(collectionOrView: EntityCollection<any, any> | CMSView) {
451
482
  }
452
483
  return trimmed ?? "Views";
453
484
  }
485
+
486
+ function areCollectionListsEqual(a: EntityCollection[], b: EntityCollection[]) {
487
+ if (a.length !== b.length) {
488
+ return false;
489
+ }
490
+ const aCopy = [...a];
491
+ const bCopy = [...b];
492
+ const aSorted = aCopy.sort((x, y) => x.id.localeCompare(y.id));
493
+ const bSorted = bCopy.sort((x, y) => x.id.localeCompare(y.id));
494
+ return aSorted.every((value, index) => areCollectionsEqual(value, bSorted[index]));
495
+ }
496
+
497
+ function areCollectionsEqual(a: EntityCollection, b: EntityCollection) {
498
+ const {
499
+ subcollections: subcollectionsA,
500
+ ...restA
501
+ } = a;
502
+ const {
503
+ subcollections: subcollectionsB,
504
+ ...restB
505
+ } = b;
506
+ if (!areCollectionListsEqual(subcollectionsA ?? [], subcollectionsB ?? [])) {
507
+ return false;
508
+ }
509
+ return equal(restA, restB);
510
+ }
@@ -9,7 +9,7 @@ export type AccessResponse = {
9
9
  message?: string;
10
10
  }
11
11
 
12
- async function makeRequest(authController: AuthController, pluginKeys: string | undefined) {
12
+ async function makeRequest(authController: AuthController, pluginKeys: string[] | undefined) {
13
13
  const firebaseToken = await authController.getAuthToken();
14
14
  return fetch(DEFAULT_SERVER + "/access_log",
15
15
  {
@@ -30,12 +30,12 @@ export function useProjectLog(authController: AuthController,
30
30
  plugins?: FireCMSPlugin<any, any, any>[]): AccessResponse | null {
31
31
  const [accessResponse, setAccessResponse] = useState<AccessResponse | null>(null);
32
32
  const accessedUserRef = useRef<string | null>(null);
33
- const pluginKeys = plugins?.map(plugin => plugin.key).join(",");
33
+ const pluginKeys = plugins?.map(plugin => plugin.key);
34
34
  useEffect(() => {
35
35
  if (authController.user && authController.user.uid !== accessedUserRef.current && !authController.initialLoading) {
36
36
  makeRequest(authController, pluginKeys).then(setAccessResponse);
37
37
  accessedUserRef.current = authController.user.uid;
38
38
  }
39
- }, [authController]);
39
+ }, [authController, pluginKeys]);
40
40
  return accessResponse;
41
41
  }
@@ -1,15 +1,13 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import equal from "react-fast-compare";
3
3
 
4
- import { AppCheckTokenResult, AuthController, Authenticator, DataSourceDelegate, StorageSource, User } from "../index";
4
+ import { AuthController, Authenticator, DataSourceDelegate, StorageSource, User } from "../index";
5
5
 
6
6
  /**
7
7
  * This hook is used internally for validating an authenticator.
8
8
  *
9
9
  * @param authController
10
10
  * @param authentication
11
- * @param getAppCheckToken
12
- * @param appCheckForceRefresh
13
11
  * @param storageSource
14
12
  * @param dataSourceDelegate
15
13
  */
@@ -17,8 +15,6 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
17
15
  disabled,
18
16
  authController,
19
17
  authenticator,
20
- getAppCheckToken,
21
- appCheckForceRefresh = false,
22
18
  storageSource,
23
19
  dataSourceDelegate
24
20
  }:
@@ -26,8 +22,6 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
26
22
  disabled?: boolean,
27
23
  authController: Controller,
28
24
  authenticator?: boolean | Authenticator<UserType, Controller>,
29
- getAppCheckToken?: (forceRefresh: boolean) => Promise<AppCheckTokenResult> | undefined,
30
- appCheckForceRefresh?: boolean,
31
25
  dataSourceDelegate: DataSourceDelegate;
32
26
  storageSource: StorageSource;
33
27
  }): {
@@ -78,20 +72,6 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
78
72
  const delegateUser = authController.user;
79
73
  console.debug("Checking authentication for user", delegateUser);
80
74
 
81
- if (getAppCheckToken) {
82
- try {
83
- if (!await getAppCheckToken(appCheckForceRefresh)) {
84
- setNotAllowedError("App Check failed.");
85
- authController.signOut();
86
- } else {
87
- console.debug("App Check success.");
88
- }
89
- } catch (e: any) {
90
- setNotAllowedError(e.message);
91
- authController.signOut();
92
- }
93
- }
94
-
95
75
  if (authenticator instanceof Function && delegateUser && !equal(checkedUserRef.current?.uid, delegateUser.uid)) {
96
76
  setAuthLoading(true);
97
77
  try {
@@ -120,7 +100,7 @@ export function useValidateAuthenticator<UserType extends User = User, Controlle
120
100
  setAuthVerified(true);
121
101
  }
122
102
 
123
- }, [disabled, authController, authenticator, getAppCheckToken, appCheckForceRefresh, dataSourceDelegate, storageSource]);
103
+ }, [disabled, authController, authenticator, dataSourceDelegate, storageSource]);
124
104
 
125
105
  useEffect(() => {
126
106
  checkAuthentication();
@@ -5,12 +5,10 @@ import {
5
5
  DeleteEntityProps,
6
6
  Entity,
7
7
  EntityCollection,
8
- EntityReference,
9
8
  EntityValues,
10
9
  FetchCollectionProps,
11
10
  FetchEntityProps,
12
11
  FilterValues,
13
- GeoPoint,
14
12
  ListenCollectionProps,
15
13
  ListenEntityProps,
16
14
  NavigationController,
@@ -207,13 +205,10 @@ export function useBuildDataSource({
207
205
 
208
206
  const properties: ResolvedProperties<M> | undefined = resolvedCollection?.properties;
209
207
 
210
- const firestoreValues = cmsToDelegateModel(
208
+ const firestoreValues = delegate.cmsToDelegateModel(
211
209
  values,
212
- delegate.buildReference,
213
- delegate.buildGeoPoint,
214
- delegate.buildDate,
215
- delegate.buildDeleteFieldValue
216
210
  );
211
+
217
212
  const updatedFirestoreValues: EntityValues<M> = properties
218
213
  ? updateDateAutoValues(
219
214
  {
@@ -316,48 +311,48 @@ export function useBuildDataSource({
316
311
  sortBy
317
312
  }
318
313
  )
319
- }, [delegate.isFilterCombinationValid])
314
+ }, [delegate.isFilterCombinationValid]),
320
315
 
321
316
  };
322
317
 
323
318
  }
324
319
 
325
- /**
326
- * Recursive function that converts Firestore data types into CMS or plain
327
- * JS types.
328
- * FireCMS uses Javascript dates internally instead of Firestore timestamps.
329
- * This makes it easier to interact with the rest of the libraries and
330
- * bindings.
331
- * Also, Firestore references are replaced with {@link EntityReference}
332
- * @param data
333
- * @param buildReference
334
- * @param buildGeoPoint
335
- * @param buildDate
336
- * @param buildDelete
337
- * @group Firestore
338
- */
339
- export function cmsToDelegateModel(data: any,
340
- buildReference: (reference: EntityReference) => any,
341
- buildGeoPoint: (geoPoint: GeoPoint) => any,
342
- buildDate: (date: Date) => any,
343
- buildDelete: () => any
344
- ): any {
345
- if (data === undefined) {
346
- return buildDelete();
347
- } else if (data === null) {
348
- return null;
349
- } else if (Array.isArray(data)) {
350
- return data.map(v => cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete));
351
- } else if (data.isEntityReference && data.isEntityReference()) {
352
- return buildReference(data);
353
- } else if (data instanceof GeoPoint) {
354
- return buildGeoPoint(data);
355
- } else if (data instanceof Date) {
356
- return buildDate(data);
357
- } else if (data && typeof data === "object") {
358
- return Object.entries(data)
359
- .map(([key, v]) => ({ [key]: cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete) }))
360
- .reduce((a, b) => ({ ...a, ...b }), {});
361
- }
362
- return data;
363
- }
320
+ // /**
321
+ // * Recursive function that converts Firestore data types into CMS or plain
322
+ // * JS types.
323
+ // * FireCMS uses Javascript dates internally instead of Firestore timestamps.
324
+ // * This makes it easier to interact with the rest of the libraries and
325
+ // * bindings.
326
+ // * Also, Firestore references are replaced with {@link EntityReference}
327
+ // * @param data
328
+ // * @param buildReference
329
+ // * @param buildGeoPoint
330
+ // * @param buildDate
331
+ // * @param buildDelete
332
+ // * @group Firestore
333
+ // */
334
+ // export function cmsToDelegateModel(data: any,
335
+ // buildReference: (reference: EntityReference) => any,
336
+ // buildGeoPoint: (geoPoint: GeoPoint) => any,
337
+ // buildDate: (date: Date) => any,
338
+ // buildDelete: () => any
339
+ // ): any {
340
+ // if (data === undefined) {
341
+ // return buildDelete();
342
+ // } else if (data === null) {
343
+ // return null;
344
+ // } else if (Array.isArray(data)) {
345
+ // return data.map(v => cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete));
346
+ // } else if (data.isEntityReference && data.isEntityReference()) {
347
+ // return buildReference(data);
348
+ // } else if (data instanceof GeoPoint) {
349
+ // return buildGeoPoint(data);
350
+ // } else if (data instanceof Date) {
351
+ // return buildDate(data);
352
+ // } else if (data && typeof data === "object") {
353
+ // return Object.entries(data)
354
+ // .map(([key, v]) => ({ [key]: cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete) }))
355
+ // .reduce((a, b) => ({ ...a, ...b }), {});
356
+ // }
357
+ // return data;
358
+ // }
@@ -43,17 +43,17 @@ export const useBuildSideEntityController = (navigation: NavigationController,
43
43
  const panel = panelsFromUrl[i];
44
44
  setTimeout(() => {
45
45
  if (i === 0)
46
- sideDialogsController.replace(propsToSidePanel(panel, navigation, smallLayout));
46
+ sideDialogsController.replace(propsToSidePanel(panel, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
47
47
  else
48
- sideDialogsController.open(propsToSidePanel(panel, navigation, smallLayout))
48
+ sideDialogsController.open(propsToSidePanel(panel, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout))
49
49
  }, 1);
50
50
  }
51
51
  } else {
52
- console.warn("Location path is not a collection path");
52
+ // console.warn("Location path is not a collection path");
53
53
  }
54
54
  initialised.current = true;
55
55
  }
56
- }, [location, navigation, sideDialogsController, smallLayout]);
56
+ }, [location, navigation.loading, navigation.isUrlCollectionPath, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, sideDialogsController, smallLayout, navigation]);
57
57
 
58
58
  const close = useCallback(() => {
59
59
  sideDialogsController.close();
@@ -76,9 +76,9 @@ export const useBuildSideEntityController = (navigation: NavigationController,
76
76
  sideDialogsController.open(propsToSidePanel({
77
77
  selectedSubPath: defaultSelectedView,
78
78
  ...props,
79
- }, navigation, smallLayout));
79
+ }, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
80
80
 
81
- }, [sideDialogsController, navigation, smallLayout]);
81
+ }, [sideDialogsController, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout]);
82
82
 
83
83
  const replace = useCallback((props: EntitySidePanelProps<any>) => {
84
84
 
@@ -86,9 +86,9 @@ export const useBuildSideEntityController = (navigation: NavigationController,
86
86
  throw Error("If you want to copy an entity you need to provide an entityId");
87
87
  }
88
88
 
89
- sideDialogsController.replace(propsToSidePanel(props, navigation, smallLayout));
89
+ sideDialogsController.replace(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
90
90
 
91
- }, [navigation, sideDialogsController, smallLayout]);
91
+ }, [navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, sideDialogsController, smallLayout]);
92
92
 
93
93
  return {
94
94
  close,
@@ -149,14 +149,17 @@ export function buildSidePanelsFromUrl(path: string, collections: EntityCollecti
149
149
  return sidePanels;
150
150
  }
151
151
 
152
- const propsToSidePanel = (props: EntitySidePanelProps<any>, navigation: NavigationController, smallLayout: boolean): SideDialogPanelProps => {
152
+ const propsToSidePanel = (props: EntitySidePanelProps<any>,
153
+ buildUrlCollectionPath: (path: string) => string,
154
+ resolveAliasesFrom: (pathWithAliases: string) => string,
155
+ smallLayout: boolean): SideDialogPanelProps => {
153
156
 
154
157
  const collectionPath = removeInitialAndTrailingSlashes(props.path);
155
158
 
156
159
  const newPath = props.entityId
157
- ? navigation.buildUrlCollectionPath(`${collectionPath}/${props.entityId}/${props.selectedSubPath || ""}`)
158
- : navigation.buildUrlCollectionPath(`${collectionPath}#${NEW_URL_HASH}`);
159
- const resolvedPath = navigation.resolveAliasesFrom(props.path);
160
+ ? buildUrlCollectionPath(`${collectionPath}/${props.entityId}/${props.selectedSubPath || ""}`)
161
+ : buildUrlCollectionPath(`${collectionPath}#${NEW_URL_HASH}`);
162
+ const resolvedPath = resolveAliasesFrom(props.path);
160
163
 
161
164
  const resolvedPanelProps: EntitySidePanelProps<any> = {
162
165
  ...props,
@@ -167,7 +170,7 @@ const propsToSidePanel = (props: EntitySidePanelProps<any>, navigation: Navigati
167
170
  key: `${props.path}/${props.entityId}`,
168
171
  component: <EntitySidePanel {...resolvedPanelProps}/>,
169
172
  urlPath: newPath,
170
- parentUrlPath: navigation.buildUrlCollectionPath(collectionPath),
173
+ parentUrlPath: buildUrlCollectionPath(collectionPath),
171
174
  width: getEntityViewWidth(props, smallLayout),
172
175
  onClose: props.onClose
173
176
  });
@@ -92,7 +92,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
92
92
  size={props.size}
93
93
  storagePathOrDownloadUrl={value}/>;
94
94
  } else if (stringProperty.markdown) {
95
- content = <Markdown source={value}/>;
95
+ content = <Markdown source={value} size={"small"}/>;
96
96
  } else {
97
97
  content = <StringPropertyPreview {...props}
98
98
  property={stringProperty}
@@ -113,17 +113,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
113
113
  content = <ArrayPropertyPreview {...props}
114
114
  value={value}
115
115
  property={property as ResolvedArrayProperty}/>;
116
- }
117
- // else if (arrayProperty.of.dataType === "map") {
118
- // content =
119
- // <ArrayOfMapsPreview propertyKey={propertyKey}
120
- // property={property as ResolvedArrayProperty}
121
- // value={value as Record<string, any>[]} // This might be wrong
122
- // entity={entity}
123
- // size={size}
124
- // />;
125
- // }
126
- else if (arrayProperty.of.dataType === "reference") {
116
+ } else if (arrayProperty.of.dataType === "reference") {
127
117
  content = <ArrayOfReferencesPreview {...props}
128
118
  value={value}
129
119
  property={property as ResolvedArrayProperty}/>;
@@ -107,6 +107,10 @@ export interface EntityCollection<M extends Record<string, any> = any, UserType
107
107
  * `subcollection:`. e.g. `subcollection:orders`.
108
108
  * - If you are using a collection group, you will also have an
109
109
  * additional `collectionGroupParent` column.
110
+ * You can use this prop to hide some properties from the table view.
111
+ * Note that if you set this prop, other ways to hide fields, like
112
+ * `hidden` in the property definition,will be ignored.
113
+ * `propertiesOrder` has precedence over `hidden`.
110
114
  */
111
115
  propertiesOrder?: Extract<keyof M, string>[];
112
116
 
@@ -523,7 +527,7 @@ export type EntityTableController<M extends Record<string, any> = any> = {
523
527
  filterValues?: FilterValues<Extract<keyof M, string>>;
524
528
  setFilterValues?: (filterValues: FilterValues<Extract<keyof M, string>>) => void;
525
529
  sortBy?: [Extract<keyof M, string>, "asc" | "desc"];
526
- setSortBy?: (sortBy: [Extract<keyof M, string>, "asc" | "desc"]) => void;
530
+ setSortBy?: (sortBy?: [Extract<keyof M, string>, "asc" | "desc"]) => void;
527
531
  searchString?: string;
528
532
  setSearchString?: (searchString?: string) => void;
529
533
  clearFilter?: () => void;
@@ -216,6 +216,7 @@ export interface DataSource {
216
216
  * @param props
217
217
  */
218
218
  isFilterCombinationValid?(props: FilterCombinationValidProps): boolean;
219
+
219
220
  }
220
221
 
221
222
  export type FilterCombinationValidProps = {
@@ -366,24 +367,26 @@ export interface DataSourceDelegate {
366
367
  * Convert a FireCMS reference to a reference that can be used by the datasource
367
368
  * @param reference
368
369
  */
369
- buildReference: (reference: EntityReference) => any,
370
+ // buildReference: (reference: EntityReference) => any,
370
371
 
371
372
  /**
372
373
  * Convert a FireCMS GeoPoint to a GeoPoint that can be used by the datasource
373
374
  * @param geoPoint
374
375
  */
375
- buildGeoPoint: (geoPoint: GeoPoint) => any,
376
+ // buildGeoPoint: (geoPoint: GeoPoint) => any,
376
377
 
377
378
  /**
378
379
  * Get the object to generate the current time in the datasource
379
380
  */
380
381
  currentTime(): any;
381
382
 
382
- buildDate: (date: Date) => any;
383
+ // buildDate: (date: Date) => any;
383
384
 
384
- buildDeleteFieldValue: () => any;
385
+ // buildDeleteFieldValue: () => any;
385
386
 
386
387
  delegateToCMSModel: (data: any) => any;
387
388
 
389
+ cmsToDelegateModel: (data: any) => any;
390
+
388
391
  setDateToMidnight: (input?: any) => any;
389
392
  }
@@ -37,7 +37,7 @@ export type EntityValues<M extends object> = M;
37
37
  /**
38
38
  * Class used to create a reference to an entity in a different path
39
39
  */
40
- export class EntityReference<M extends Record<string, any> = any> {
40
+ export class EntityReference {
41
41
  /**
42
42
  * ID of the entity
43
43
  */
@@ -78,3 +78,11 @@ export class GeoPoint {
78
78
  this.longitude = longitude;
79
79
  }
80
80
  }
81
+
82
+ export class Vector {
83
+ readonly value: number[];
84
+
85
+ constructor(value: number[]) {
86
+ this.value = value;
87
+ }
88
+ }
@@ -23,7 +23,6 @@ export * from "./plugins";
23
23
  export * from "./analytics";
24
24
  export * from "./firecms";
25
25
  export * from "./roles";
26
- export * from "./appcheck";
27
26
  export * from "./export_import";
28
27
  export * from "./modify_collections";
29
28
  export * from "./analytics_controller";