@firecms/core 3.0.0-canary.9 → 3.0.0-canary.91

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 (247) hide show
  1. package/README.md +1 -1
  2. package/dist/app/AppBar.d.ts +12 -0
  3. package/dist/app/Drawer.d.ts +17 -0
  4. package/dist/app/Scaffold.d.ts +30 -0
  5. package/dist/app/index.d.ts +4 -0
  6. package/dist/app/useApp.d.ts +16 -0
  7. package/dist/components/CircularProgressCenter.d.ts +1 -1
  8. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  9. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -12
  10. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  11. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
  12. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
  13. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  14. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
  15. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  16. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  17. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  18. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -2
  19. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  20. package/dist/components/EntityPreview.d.ts +5 -4
  21. package/dist/components/ErrorView.d.ts +1 -1
  22. package/dist/components/HomePage/DefaultHomePage.d.ts +1 -1
  23. package/dist/components/HomePage/NavigationCardBinding.d.ts +1 -1
  24. package/dist/components/ReferenceWidget.d.ts +3 -1
  25. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  26. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  27. package/dist/components/VirtualTable/VirtualTableProps.d.ts +15 -12
  28. package/dist/components/VirtualTable/types.d.ts +3 -3
  29. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
  30. package/dist/components/common/index.d.ts +1 -0
  31. package/dist/components/common/table_height.d.ts +5 -0
  32. package/dist/components/common/types.d.ts +4 -6
  33. package/dist/components/common/useColumnsIds.d.ts +2 -1
  34. package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
  35. package/dist/components/index.d.ts +2 -1
  36. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  37. package/dist/{components/FireCMSAppBar.d.ts → core/DefaultAppBar.d.ts} +6 -9
  38. package/dist/core/DefaultDrawer.d.ts +19 -0
  39. package/dist/core/DrawerNavigationItem.d.ts +9 -0
  40. package/dist/core/EntityEditView.d.ts +17 -3
  41. package/dist/core/FireCMS.d.ts +1 -1
  42. package/dist/core/NavigationRoutes.d.ts +3 -3
  43. package/dist/core/index.d.ts +3 -4
  44. package/dist/form/PropertiesForm.d.ts +8 -0
  45. package/dist/form/components/ErrorFocus.d.ts +1 -1
  46. package/dist/form/components/FieldHelperText.d.ts +3 -3
  47. package/dist/form/components/StorageItemPreview.d.ts +4 -4
  48. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  49. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +2 -4
  50. package/dist/form/index.d.ts +0 -2
  51. package/dist/hooks/data/delete.d.ts +2 -2
  52. package/dist/hooks/data/save.d.ts +2 -3
  53. package/dist/hooks/data/useDataSource.d.ts +1 -1
  54. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  55. package/dist/hooks/index.d.ts +2 -0
  56. package/dist/hooks/useBuildNavigationController.d.ts +1 -2
  57. package/dist/hooks/useProjectLog.d.ts +2 -2
  58. package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.es.js +15825 -12165
  61. package/dist/index.es.js.map +1 -1
  62. package/dist/index.umd.js +19684 -7
  63. package/dist/index.umd.js.map +1 -1
  64. package/dist/internal/useBuildDataSource.d.ts +1 -16
  65. package/dist/internal/useRestoreScroll.d.ts +1 -1
  66. package/dist/preview/PropertyPreviewProps.d.ts +6 -4
  67. package/dist/preview/components/ReferencePreview.d.ts +2 -1
  68. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  69. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  70. package/dist/types/auth.d.ts +26 -2
  71. package/dist/types/collections.d.ts +31 -7
  72. package/dist/types/datasource.d.ts +34 -20
  73. package/dist/types/entities.d.ts +5 -1
  74. package/dist/types/entity_actions.d.ts +14 -0
  75. package/dist/types/entity_callbacks.d.ts +2 -2
  76. package/dist/types/fields.d.ts +31 -30
  77. package/dist/types/index.d.ts +0 -1
  78. package/dist/types/navigation.d.ts +5 -5
  79. package/dist/types/plugins.d.ts +16 -6
  80. package/dist/types/properties.d.ts +17 -4
  81. package/dist/types/storage.d.ts +11 -3
  82. package/dist/util/collections.d.ts +1 -1
  83. package/dist/util/entities.d.ts +1 -1
  84. package/dist/util/icon_synonyms.d.ts +1 -97
  85. package/dist/util/icons.d.ts +2 -2
  86. package/dist/util/navigation_utils.d.ts +2 -2
  87. package/dist/util/objects.d.ts +1 -1
  88. package/dist/util/plurals.d.ts +0 -2
  89. package/dist/util/resolutions.d.ts +13 -13
  90. package/dist/util/storage.d.ts +23 -2
  91. package/dist/util/useStorageUploadController.d.ts +1 -1
  92. package/dist/util/useTraceUpdate.d.ts +1 -0
  93. package/package.json +130 -119
  94. package/src/app/AppBar.tsx +18 -0
  95. package/src/app/Drawer.tsx +25 -0
  96. package/src/app/Scaffold.tsx +249 -0
  97. package/src/app/index.ts +4 -0
  98. package/src/app/useApp.tsx +32 -0
  99. package/src/components/CircularProgressCenter.tsx +1 -1
  100. package/src/components/ClearFilterSortButton.tsx +41 -0
  101. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +9 -18
  102. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +21 -20
  103. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +10 -6
  104. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +38 -34
  105. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  106. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +11 -2
  107. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +14 -6
  108. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
  109. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +16 -12
  110. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +4 -5
  111. package/src/components/EntityCollectionView/EntityCollectionView.tsx +69 -45
  112. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  113. package/src/components/EntityCollectionView/useSelectionController.tsx +19 -7
  114. package/src/components/EntityPreview.tsx +15 -9
  115. package/src/components/EntityView.tsx +5 -5
  116. package/src/components/ErrorView.tsx +1 -1
  117. package/src/components/HomePage/DefaultHomePage.tsx +3 -3
  118. package/src/components/HomePage/NavigationCard.tsx +3 -3
  119. package/src/components/HomePage/NavigationCardBinding.tsx +1 -1
  120. package/src/components/HomePage/SmallNavigationCard.tsx +5 -5
  121. package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
  122. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +1 -0
  123. package/src/components/ReferenceWidget.tsx +22 -12
  124. package/src/components/SearchIconsView.tsx +5 -5
  125. package/src/components/SelectableTable/SelectableTable.tsx +7 -7
  126. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  127. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +22 -7
  128. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +28 -6
  129. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  130. package/src/components/VirtualTable/VirtualTable.tsx +70 -37
  131. package/src/components/VirtualTable/VirtualTableCell.tsx +1 -1
  132. package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
  133. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  134. package/src/components/VirtualTable/VirtualTableProps.tsx +18 -14
  135. package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
  136. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  137. package/src/components/VirtualTable/types.tsx +2 -3
  138. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +2 -2
  139. package/src/components/common/index.ts +1 -0
  140. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  141. package/src/components/common/types.tsx +4 -6
  142. package/src/components/common/useColumnsIds.tsx +23 -2
  143. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +11 -0
  144. package/src/components/common/useTableSearchHelper.ts +52 -12
  145. package/src/components/index.tsx +2 -1
  146. package/src/contexts/AuthControllerContext.tsx +1 -1
  147. package/src/contexts/DialogsProvider.tsx +2 -2
  148. package/src/{components/FireCMSAppBar.tsx → core/DefaultAppBar.tsx} +52 -37
  149. package/src/core/DefaultDrawer.tsx +177 -0
  150. package/src/core/DrawerNavigationItem.tsx +62 -0
  151. package/src/core/EntityEditView.tsx +711 -139
  152. package/src/core/EntitySidePanel.tsx +1 -2
  153. package/src/core/FireCMS.tsx +39 -44
  154. package/src/core/NavigationRoutes.tsx +7 -8
  155. package/src/core/field_configs.tsx +2 -3
  156. package/src/core/index.tsx +3 -4
  157. package/src/form/PropertiesForm.tsx +81 -0
  158. package/src/form/PropertyFieldBinding.tsx +29 -7
  159. package/src/form/components/FieldHelperText.tsx +3 -3
  160. package/src/form/components/StorageItemPreview.tsx +20 -11
  161. package/src/form/components/StorageUploadProgress.tsx +3 -3
  162. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +8 -5
  163. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +8 -5
  164. package/src/form/field_bindings/BlockFieldBinding.tsx +2 -2
  165. package/src/form/field_bindings/KeyValueFieldBinding.tsx +44 -39
  166. package/src/form/field_bindings/MapFieldBinding.tsx +11 -3
  167. package/src/form/field_bindings/MarkdownFieldBinding.tsx +2 -2
  168. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -9
  169. package/src/form/field_bindings/ReferenceFieldBinding.tsx +15 -13
  170. package/src/form/field_bindings/RepeatFieldBinding.tsx +10 -7
  171. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -3
  172. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +22 -43
  173. package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -1
  174. package/src/form/index.tsx +4 -4
  175. package/src/form/validation.ts +1 -17
  176. package/src/hooks/data/delete.ts +3 -3
  177. package/src/hooks/data/save.ts +4 -2
  178. package/src/hooks/data/useDataSource.tsx +2 -2
  179. package/src/hooks/data/useEntityFetch.tsx +3 -3
  180. package/src/hooks/index.tsx +3 -0
  181. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  182. package/src/hooks/useBuildModeController.tsx +11 -5
  183. package/src/hooks/useBuildNavigationController.tsx +137 -61
  184. package/src/hooks/useProjectLog.tsx +21 -8
  185. package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
  186. package/src/hooks/useValidateAuthenticator.tsx +115 -0
  187. package/src/index.ts +1 -0
  188. package/src/internal/useBuildDataSource.ts +56 -49
  189. package/src/internal/useBuildSideEntityController.tsx +88 -21
  190. package/src/preview/PropertyPreview.tsx +9 -16
  191. package/src/preview/PropertyPreviewProps.tsx +4 -8
  192. package/src/preview/components/BooleanPreview.tsx +4 -2
  193. package/src/preview/components/EnumValuesChip.tsx +1 -1
  194. package/src/preview/components/ImagePreview.tsx +21 -33
  195. package/src/preview/components/ReferencePreview.tsx +23 -23
  196. package/src/preview/components/StorageThumbnail.tsx +5 -1
  197. package/src/preview/components/UrlComponentPreview.tsx +44 -11
  198. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
  199. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -1
  200. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
  201. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
  202. package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -3
  203. package/src/preview/property_previews/ArrayPropertyPreview.tsx +2 -3
  204. package/src/preview/property_previews/MapPropertyPreview.tsx +5 -5
  205. package/src/preview/property_previews/StringPropertyPreview.tsx +2 -2
  206. package/src/types/auth.tsx +35 -2
  207. package/src/types/collections.ts +37 -8
  208. package/src/types/customization_controller.tsx +0 -1
  209. package/src/types/datasource.ts +41 -24
  210. package/src/types/entities.ts +9 -1
  211. package/src/types/entity_actions.tsx +16 -3
  212. package/src/types/entity_callbacks.ts +2 -2
  213. package/src/types/fields.tsx +33 -33
  214. package/src/types/index.ts +0 -1
  215. package/src/types/navigation.ts +6 -7
  216. package/src/types/plugins.tsx +18 -8
  217. package/src/types/properties.ts +22 -6
  218. package/src/types/storage.ts +12 -3
  219. package/src/util/collections.ts +1 -1
  220. package/src/util/entities.ts +5 -4
  221. package/src/util/enums.ts +1 -1
  222. package/src/util/icon_list.ts +2 -2
  223. package/src/util/icon_synonyms.ts +3 -99
  224. package/src/util/navigation_utils.ts +6 -6
  225. package/src/util/objects.ts +25 -28
  226. package/src/util/permissions.ts +1 -0
  227. package/src/util/plurals.ts +0 -2
  228. package/src/util/resolutions.ts +32 -31
  229. package/src/util/storage.ts +75 -21
  230. package/src/util/strings.ts +2 -2
  231. package/src/util/useStorageUploadController.tsx +21 -3
  232. package/src/util/useTraceUpdate.tsx +2 -1
  233. package/dist/components/VirtualTable/common.d.ts +0 -2
  234. package/dist/core/Drawer.d.ts +0 -23
  235. package/dist/core/Scaffold.d.ts +0 -55
  236. package/dist/core/SideEntityView.d.ts +0 -7
  237. package/dist/form/EntityForm.d.ts +0 -77
  238. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  239. package/dist/internal/useLocaleConfig.d.ts +0 -1
  240. package/dist/types/appcheck.d.ts +0 -26
  241. package/src/core/Drawer.tsx +0 -191
  242. package/src/core/Scaffold.tsx +0 -281
  243. package/src/core/SideEntityView.tsx +0 -38
  244. package/src/form/EntityForm.tsx +0 -728
  245. package/src/internal/useBuildCustomizationController.tsx +0 -5
  246. package/src/internal/useLocaleConfig.tsx +0 -18
  247. package/src/types/appcheck.ts +0 -29
@@ -0,0 +1,115 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import equal from "react-fast-compare";
3
+
4
+ import { AuthController, Authenticator, DataSourceDelegate, StorageSource, User } from "../index";
5
+
6
+ /**
7
+ * This hook is used internally for validating an authenticator.
8
+ *
9
+ * @param authController
10
+ * @param authentication
11
+ * @param storageSource
12
+ * @param dataSourceDelegate
13
+ */
14
+ export function useValidateAuthenticator<UserType extends User = User, Controller extends AuthController<UserType> = AuthController<UserType>>
15
+ ({
16
+ disabled,
17
+ authController,
18
+ authenticator,
19
+ storageSource,
20
+ dataSourceDelegate
21
+ }:
22
+ {
23
+ disabled?: boolean,
24
+ authController: Controller,
25
+ authenticator?: boolean | Authenticator<UserType, Controller>,
26
+ dataSourceDelegate: DataSourceDelegate;
27
+ storageSource: StorageSource;
28
+ }): {
29
+ canAccessMainView: boolean,
30
+ authLoading: boolean,
31
+ notAllowedError: any,
32
+ authVerified: boolean,
33
+ } {
34
+
35
+ const authenticationEnabled = Boolean(authenticator);
36
+
37
+ const [authLoading, setAuthLoading] = useState<boolean>(authenticationEnabled);
38
+ const [notAllowedError, setNotAllowedError] = useState<any>(false);
39
+ const [authVerified, setAuthVerified] = useState<boolean>(!authenticationEnabled || Boolean(authController.loginSkipped));
40
+
41
+ const canAccessMainView = (authVerified) &&
42
+ (!authenticationEnabled || Boolean(authController.user) || Boolean(authController.loginSkipped)) &&
43
+ !notAllowedError;
44
+
45
+ useEffect(() => {
46
+ if (authController.loginSkipped)
47
+ setAuthVerified(true);
48
+ }, [authController.loginSkipped]);
49
+
50
+ /**
51
+ * We use this ref to check the authentication only if the user has
52
+ * changed.
53
+ */
54
+ const checkedUserRef = useRef<User | undefined>();
55
+
56
+ const checkAuthentication = useCallback(async () => {
57
+
58
+ if (disabled) {
59
+ return;
60
+ }
61
+
62
+ if (authController.initialLoading) {
63
+ return;
64
+ }
65
+
66
+ if (!authController.user && !authController.loginSkipped) {
67
+ checkedUserRef.current = undefined;
68
+ setAuthLoading(false);
69
+ setAuthVerified(false);
70
+ return;
71
+ }
72
+
73
+ const delegateUser = authController.user;
74
+
75
+ if (authenticator instanceof Function && delegateUser && !equal(checkedUserRef.current?.uid, delegateUser.uid)) {
76
+ setAuthLoading(true);
77
+ try {
78
+ const allowed = await authenticator({
79
+ user: delegateUser,
80
+ authController,
81
+ dataSourceDelegate,
82
+ storageSource
83
+ });
84
+ if (!allowed) {
85
+ authController.signOut();
86
+ setNotAllowedError(true);
87
+ }
88
+ } catch (e) {
89
+ setNotAllowedError(e);
90
+ authController.signOut();
91
+ }
92
+ setAuthLoading(false);
93
+ setAuthVerified(true);
94
+ checkedUserRef.current = delegateUser;
95
+ } else {
96
+ setAuthLoading(false);
97
+ }
98
+
99
+ if (!authController.initialLoading && !delegateUser) {
100
+ setAuthVerified(true);
101
+ }
102
+
103
+ }, [disabled, authController, authenticator, dataSourceDelegate, storageSource]);
104
+
105
+ useEffect(() => {
106
+ checkAuthentication();
107
+ }, [checkAuthentication]);
108
+
109
+ return {
110
+ canAccessMainView,
111
+ authLoading: authenticationEnabled && authLoading,
112
+ notAllowedError,
113
+ authVerified
114
+ }
115
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./core";
2
+ export * from "./app";
2
3
  export * from "./types";
3
4
  export * from "./form";
4
5
  export * from "./preview";
@@ -5,12 +5,11 @@ 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,
12
+ FireCMSContext,
14
13
  ListenCollectionProps,
15
14
  ListenEntityProps,
16
15
  NavigationController,
@@ -207,20 +206,17 @@ export function useBuildDataSource({
207
206
 
208
207
  const properties: ResolvedProperties<M> | undefined = resolvedCollection?.properties;
209
208
 
210
- const firestoreValues = cmsToDelegateModel(
209
+ const firestoreValues = delegate.cmsToDelegateModel(
211
210
  values,
212
- delegate.buildReference,
213
- delegate.buildGeoPoint,
214
- delegate.buildDate,
215
- delegate.buildDeleteFieldValue
216
211
  );
212
+
217
213
  const updatedFirestoreValues: EntityValues<M> = properties
218
214
  ? updateDateAutoValues(
219
215
  {
220
216
  inputValues: firestoreValues,
221
217
  properties,
222
218
  status,
223
- timestampNowValue: delegate.currentTime(),
219
+ timestampNowValue: delegate.currentTime?.() ?? new Date(),
224
220
  setDateToMidnight: delegate.setDateToMidnight
225
221
  })
226
222
  : firestoreValues;
@@ -294,7 +290,7 @@ export function useBuildDataSource({
294
290
  filter,
295
291
  orderBy,
296
292
  order,
297
- isCollectionGroup: Boolean(collection.collectionGroup) ?? false
293
+ isCollectionGroup: Boolean(collection.collectionGroup)
298
294
  });
299
295
  } : undefined,
300
296
 
@@ -316,48 +312,59 @@ export function useBuildDataSource({
316
312
  sortBy
317
313
  }
318
314
  )
319
- }, [delegate.isFilterCombinationValid])
315
+ }, [delegate.isFilterCombinationValid]),
316
+
317
+ initTextSearch: useCallback(async (props: {
318
+ context: FireCMSContext,
319
+ path: string,
320
+ collection: EntityCollection,
321
+ parentCollectionIds?: string[]
322
+ }): Promise<boolean> => {
323
+ if (!delegate.initTextSearch)
324
+ return false;
325
+ return delegate.initTextSearch(props)
326
+ }, [delegate.initTextSearch]),
320
327
 
321
328
  };
322
329
 
323
330
  }
324
331
 
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
- }
332
+ // /**
333
+ // * Recursive function that converts Firestore data types into CMS or plain
334
+ // * JS types.
335
+ // * FireCMS uses Javascript dates internally instead of Firestore timestamps.
336
+ // * This makes it easier to interact with the rest of the libraries and
337
+ // * bindings.
338
+ // * Also, Firestore references are replaced with {@link EntityReference}
339
+ // * @param data
340
+ // * @param buildReference
341
+ // * @param buildGeoPoint
342
+ // * @param buildDate
343
+ // * @param buildDelete
344
+ // * @group Firestore
345
+ // */
346
+ // export function cmsToDelegateModel(data: any,
347
+ // buildReference: (reference: EntityReference) => any,
348
+ // buildGeoPoint: (geoPoint: GeoPoint) => any,
349
+ // buildDate: (date: Date) => any,
350
+ // buildDelete: () => any
351
+ // ): any {
352
+ // if (data === undefined) {
353
+ // return buildDelete();
354
+ // } else if (data === null) {
355
+ // return null;
356
+ // } else if (Array.isArray(data)) {
357
+ // return data.map(v => cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete));
358
+ // } else if (data.isEntityReference && data.isEntityReference()) {
359
+ // return buildReference(data);
360
+ // } else if (data instanceof GeoPoint) {
361
+ // return buildGeoPoint(data);
362
+ // } else if (data instanceof Date) {
363
+ // return buildDate(data);
364
+ // } else if (data && typeof data === "object") {
365
+ // return Object.entries(data)
366
+ // .map(([key, v]) => ({ [key]: cmsToDelegateModel(v, buildReference, buildGeoPoint, buildDate, buildDelete) }))
367
+ // .reduce((a, b) => ({ ...a, ...b }), {});
368
+ // }
369
+ // return data;
370
+ // }
@@ -2,14 +2,15 @@ import { useCallback, useEffect, useRef } from "react";
2
2
  import {
3
3
  EntityCollection,
4
4
  EntitySidePanelProps,
5
- NavigationController,
5
+ NavigationController, PropertyConfig,
6
+ ResolvedProperty,
6
7
  SideDialogPanelProps,
7
8
  SideDialogsController,
8
9
  SideEntityController
9
10
  } from "../types";
10
11
  import { getNavigationEntriesFromPathInternal, NavigationViewInternal } from "../util/navigation_from_path";
11
12
  import { useLocation } from "react-router-dom";
12
- import { removeInitialAndTrailingSlashes, resolveDefaultSelectedView } from "../util";
13
+ import { removeInitialAndTrailingSlashes, resolveCollection, resolveDefaultSelectedView } from "../util";
13
14
  import { ADDITIONAL_TAB_WIDTH, CONTAINER_FULL_WIDTH, FORM_CONTAINER_WIDTH } from "./common";
14
15
  import { useLargeLayout } from "../hooks";
15
16
  import { EntitySidePanel } from "../core/EntitySidePanel";
@@ -19,8 +20,67 @@ const NEW_URL_HASH = "new";
19
20
  export function getEntityViewWidth(props: EntitySidePanelProps<any>, small: boolean): string {
20
21
  if (small) return CONTAINER_FULL_WIDTH;
21
22
  const mainViewSelected = !props.selectedSubPath;
22
- const resolvedWidth: string | undefined = typeof props.width === "number" ? `${props.width}px` : props.width;
23
- return !mainViewSelected ? `calc(${ADDITIONAL_TAB_WIDTH} + ${resolvedWidth ?? FORM_CONTAINER_WIDTH})` : resolvedWidth ?? FORM_CONTAINER_WIDTH
23
+ let resolvedWidth: string | undefined;
24
+ if (props.width) {
25
+ resolvedWidth = typeof props.width === "number" ? `${props.width}px` : props.width;
26
+ } else if (props.collection?.sideDialogWidth) {
27
+ resolvedWidth = typeof props.collection.sideDialogWidth === "number" ? `${props.collection.sideDialogWidth}px` : props.collection.sideDialogWidth;
28
+ }
29
+
30
+ if (!mainViewSelected) {
31
+ return `calc(${ADDITIONAL_TAB_WIDTH} + ${resolvedWidth ?? FORM_CONTAINER_WIDTH})`
32
+ } else {
33
+ if (resolvedWidth) {
34
+ return resolvedWidth
35
+ } else if (!props.collection) {
36
+ return FORM_CONTAINER_WIDTH;
37
+ } else {
38
+ return calculateCollectionDesiredWidth(props.collection);
39
+ }
40
+ }
41
+ }
42
+
43
+ const collectionViewWidthCache: { [key: string]: string } = {};
44
+
45
+ function calculateCollectionDesiredWidth(collection: EntityCollection<any>): string {
46
+ if (collectionViewWidthCache[collection.id]) {
47
+ return collectionViewWidthCache[collection.id];
48
+ }
49
+ const resolvedCollection = resolveCollection({
50
+ collection,
51
+ path: "__ignored",
52
+ ignoreMissingFields: true
53
+ });
54
+
55
+ let result = FORM_CONTAINER_WIDTH
56
+ if (resolvedCollection?.properties) {
57
+ const values = Object.values(resolvedCollection.properties).map((p: ResolvedProperty) => getNestedPropertiesDepth(p));
58
+ const maxDepth = Math.max(...values);
59
+ if (maxDepth < 3) {
60
+ result = FORM_CONTAINER_WIDTH;
61
+ } else {
62
+ result = 768 + 32 * (maxDepth - 2) + "px";
63
+ }
64
+ }
65
+ collectionViewWidthCache[collection.id] = result;
66
+ return result;
67
+ }
68
+
69
+ function getNestedPropertiesDepth(property: ResolvedProperty, accumulator: number = 0): number {
70
+ if (property.dataType === "map" && property.properties) {
71
+ const values = Object.values(property.properties).flatMap((property) => getNestedPropertiesDepth(property, accumulator + 1));
72
+ return Math.max(...values);
73
+ } else if (property.dataType === "array" && property.oneOf) {
74
+ return accumulator + 3;
75
+ } else if (property.dataType === "array" && property.of) {
76
+ if (Array.isArray(property.of)) {
77
+ return Math.max(...property.of.map((p) => getNestedPropertiesDepth(p, accumulator + 1)));
78
+ } else {
79
+ return getNestedPropertiesDepth(property.of, accumulator + 1);
80
+ }
81
+ } else {
82
+ return accumulator + 1;
83
+ }
24
84
  }
25
85
 
26
86
  export const useBuildSideEntityController = (navigation: NavigationController,
@@ -34,23 +94,26 @@ export const useBuildSideEntityController = (navigation: NavigationController,
34
94
  // only on initialisation, create panels from URL
35
95
  useEffect(() => {
36
96
  if (!navigation.loading && !initialised.current) {
97
+ console.debug("Initialising side entity controller");
37
98
  if (navigation.isUrlCollectionPath(location.pathname)) {
38
99
  const newFlag = location.hash === `#${NEW_URL_HASH}`;
39
100
  const entityOrCollectionPath = navigation.urlPathToDataPath(location.pathname);
40
101
  const panelsFromUrl = buildSidePanelsFromUrl(entityOrCollectionPath, navigation.collections ?? [], newFlag);
41
102
  for (let i = 0; i < panelsFromUrl.length; i++) {
42
- const panel = panelsFromUrl[i];
103
+ const props = panelsFromUrl[i];
43
104
  setTimeout(() => {
44
105
  if (i === 0)
45
- sideDialogsController.replace(propsToSidePanel(panel, navigation, smallLayout));
106
+ sideDialogsController.replace(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
46
107
  else
47
- sideDialogsController.open(propsToSidePanel(panel, navigation, smallLayout))
108
+ sideDialogsController.open(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout))
48
109
  }, 1);
49
110
  }
111
+ } else {
112
+ // console.warn("Location path is not a collection path");
50
113
  }
51
114
  initialised.current = true;
52
115
  }
53
- }, [location, navigation, sideDialogsController, smallLayout]);
116
+ }, [location, navigation.loading, navigation.isUrlCollectionPath, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, sideDialogsController, smallLayout, navigation]);
54
117
 
55
118
  const close = useCallback(() => {
56
119
  sideDialogsController.close();
@@ -73,9 +136,9 @@ export const useBuildSideEntityController = (navigation: NavigationController,
73
136
  sideDialogsController.open(propsToSidePanel({
74
137
  selectedSubPath: defaultSelectedView,
75
138
  ...props,
76
- }, navigation, smallLayout));
139
+ }, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
77
140
 
78
- }, [sideDialogsController, navigation, smallLayout]);
141
+ }, [sideDialogsController, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout]);
79
142
 
80
143
  const replace = useCallback((props: EntitySidePanelProps<any>) => {
81
144
 
@@ -83,9 +146,9 @@ export const useBuildSideEntityController = (navigation: NavigationController,
83
146
  throw Error("If you want to copy an entity you need to provide an entityId");
84
147
  }
85
148
 
86
- sideDialogsController.replace(propsToSidePanel(props, navigation, smallLayout));
149
+ sideDialogsController.replace(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, smallLayout));
87
150
 
88
- }, [navigation, sideDialogsController, smallLayout]);
151
+ }, [navigation.buildUrlCollectionPath, navigation.resolveAliasesFrom, sideDialogsController, smallLayout]);
89
152
 
90
153
  return {
91
154
  close,
@@ -116,7 +179,8 @@ export function buildSidePanelsFromUrl(path: string, collections: EntityCollecti
116
179
  sidePanels.push({
117
180
  path: navigationEntry.path,
118
181
  entityId: navigationEntry.entityId,
119
- copy: false
182
+ copy: false,
183
+ width: navigationEntry.parentCollection?.sideDialogWidth
120
184
  }
121
185
  );
122
186
  } else if (navigationEntry.type === "custom_view") {
@@ -146,27 +210,30 @@ export function buildSidePanelsFromUrl(path: string, collections: EntityCollecti
146
210
  return sidePanels;
147
211
  }
148
212
 
149
- const propsToSidePanel = (props: EntitySidePanelProps<any>, navigation: NavigationController, smallLayout: boolean): SideDialogPanelProps => {
213
+ const propsToSidePanel = (props: EntitySidePanelProps<any>,
214
+ buildUrlCollectionPath: (path: string) => string,
215
+ resolveAliasesFrom: (pathWithAliases: string) => string,
216
+ smallLayout: boolean): SideDialogPanelProps => {
150
217
 
151
218
  const collectionPath = removeInitialAndTrailingSlashes(props.path);
152
219
 
153
220
  const newPath = props.entityId
154
- ? navigation.buildUrlCollectionPath(`${collectionPath}/${props.entityId}/${props.selectedSubPath || ""}`)
155
- : navigation.buildUrlCollectionPath(`${collectionPath}#${NEW_URL_HASH}`);
156
- const resolvedPath = navigation.resolveAliasesFrom(props.path);
221
+ ? buildUrlCollectionPath(`${collectionPath}/${props.entityId}/${props.selectedSubPath || ""}`)
222
+ : buildUrlCollectionPath(`${collectionPath}#${NEW_URL_HASH}`);
223
+ const resolvedPath = resolveAliasesFrom(props.path);
157
224
 
158
225
  const resolvedPanelProps: EntitySidePanelProps<any> = {
159
226
  ...props,
160
- path: resolvedPath
227
+ path: resolvedPath,
161
228
  };
162
229
 
163
- return ({
230
+ return {
164
231
  key: `${props.path}/${props.entityId}`,
165
232
  component: <EntitySidePanel {...resolvedPanelProps}/>,
166
233
  urlPath: newPath,
167
- parentUrlPath: navigation.buildUrlCollectionPath(collectionPath),
234
+ parentUrlPath: buildUrlCollectionPath(collectionPath),
168
235
  width: getEntityViewWidth(props, smallLayout),
169
236
  onClose: props.onClose
170
- });
237
+ };
171
238
  }
172
239
  ;
@@ -47,13 +47,12 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
47
47
  size,
48
48
  height,
49
49
  width,
50
- // entity
50
+ interactive
51
51
  } = props;
52
52
 
53
53
  const property = resolveProperty({
54
54
  propertyKey,
55
55
  propertyOrBuilder: inputProperty,
56
- propertyValue: value,
57
56
  fields: customizationController.propertyConfigs
58
57
  });
59
58
 
@@ -85,14 +84,17 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
85
84
  content =
86
85
  <UrlComponentPreview size={props.size}
87
86
  url={value}
87
+ interactive={interactive}
88
88
  previewType={stringProperty.url}/>;
89
89
  } else if (stringProperty.storage) {
90
+ const filePath = stringProperty.storage.previewUrl ? stringProperty.storage.previewUrl(value) : value;
90
91
  content = <StorageThumbnail
92
+ interactive={interactive}
91
93
  storeUrl={property.storage?.storeUrl ?? false}
92
94
  size={props.size}
93
- storagePathOrDownloadUrl={value}/>;
95
+ storagePathOrDownloadUrl={filePath}/>;
94
96
  } else if (stringProperty.markdown) {
95
- content = <Markdown source={value}/>;
97
+ content = <Markdown source={value} size={"small"}/>;
96
98
  } else {
97
99
  content = <StringPropertyPreview {...props}
98
100
  property={stringProperty}
@@ -113,17 +115,7 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
113
115
  content = <ArrayPropertyPreview {...props}
114
116
  value={value}
115
117
  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") {
118
+ } else if (arrayProperty.of.dataType === "reference") {
127
119
  content = <ArrayOfReferencesPreview {...props}
128
120
  value={value}
129
121
  property={property as ResolvedArrayProperty}/>;
@@ -182,8 +174,9 @@ export const PropertyPreview = React.memo(function PropertyPreview<T extends CMS
182
174
  content = <ReferencePreview
183
175
  disabled={!property.path}
184
176
  previewProperties={property.previewProperties}
177
+ includeId={property.includeId}
178
+ includeEntityLink={property.includeEntityLink}
185
179
  size={props.size}
186
- // onClick={props.onClick}
187
180
  reference={value as EntityReference}
188
181
  />;
189
182
  } else {
@@ -8,7 +8,7 @@ export type PreviewSize = "medium" | "small" | "tiny";
8
8
  /**
9
9
  * @group Preview components
10
10
  */
11
- export interface PropertyPreviewProps<T extends CMSType = any, CustomProps = any, M extends Record<string, any> = Record<string, any>> {
11
+ export interface PropertyPreviewProps<T extends CMSType = any, CustomProps = any> {
12
12
  /**
13
13
  * Name of the property
14
14
  */
@@ -24,11 +24,6 @@ export interface PropertyPreviewProps<T extends CMSType = any, CustomProps = any
24
24
  */
25
25
  property: Property<T> | ResolvedProperty<T>;
26
26
 
27
- /**
28
- * Click handler
29
- */
30
- // onClick?: () => void;
31
-
32
27
  /**
33
28
  * Desired size of the preview, depending on the context.
34
29
  */
@@ -52,8 +47,9 @@ export interface PropertyPreviewProps<T extends CMSType = any, CustomProps = any
52
47
  customProps?: CustomProps;
53
48
 
54
49
  /**
55
- * Entity this property refers to
50
+ * If the preview should be interactive or not.
51
+ * This applies only to videos.
56
52
  */
57
- // entity?: Entity<M>;
53
+ interactive?: boolean;
58
54
 
59
55
  }
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { Checkbox } from "@firecms/ui";
2
+ import { Checkbox, cls } from "@firecms/ui";
3
3
  import { PreviewSize } from "../PropertyPreviewProps";
4
4
  import { Property } from "../../types";
5
5
 
@@ -17,8 +17,10 @@ export function BooleanPreview({
17
17
  }): React.ReactElement {
18
18
  return <div className={"flex flex-row gap-2 items-center"}>
19
19
  <Checkbox checked={value}
20
+ padding={false}
20
21
  size={size}
21
22
  color={"secondary"}/>
22
- {property.name && <span className={size === "tiny" ? "text-sm" : ""}>{property.name}</span>}
23
+ {property.name && <span
24
+ className={cls("text-text-secondary dark:text-text-secondary-dark", size === "tiny" ? "text-sm" : "")}>{property.name}</span>}
23
25
  </div>;
24
26
  }
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
+ import { Chip } from "@firecms/ui";
2
3
  import { EnumValues } from "../../types";
3
4
  import { buildEnumLabel, enumToObjectEntries, getColorScheme, getLabelOrConfigFrom } from "../../util/enums";
4
- import { Chip } from "@firecms/ui";
5
5
 
6
6
  export interface EnumValuesChipProps {
7
7
  enumValues?: EnumValues;