@firecms/core 3.0.0-canary.7 → 3.0.0-canary.70

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 (271) hide show
  1. package/README.md +2 -2
  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/ClearFilterSortButton.d.ts +5 -0
  8. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
  9. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  10. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +5 -3
  11. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +3 -2
  12. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  13. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +2 -0
  14. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  15. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  16. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  17. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -3
  18. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  19. package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
  20. package/dist/components/EntityPreview.d.ts +26 -7
  21. package/dist/components/EntityView.d.ts +11 -0
  22. package/dist/components/FieldCaption.d.ts +5 -0
  23. package/dist/components/HomePage/NavigationCard.d.ts +8 -0
  24. package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
  25. package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
  26. package/dist/components/HomePage/index.d.ts +3 -1
  27. package/dist/components/ReferenceWidget.d.ts +3 -1
  28. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  29. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  30. package/dist/components/VirtualTable/VirtualTableProps.d.ts +6 -7
  31. package/dist/components/VirtualTable/types.d.ts +3 -3
  32. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -1
  33. package/dist/components/common/index.d.ts +1 -0
  34. package/dist/components/common/table_height.d.ts +5 -0
  35. package/dist/components/common/types.d.ts +4 -6
  36. package/dist/components/common/useDataSourceEntityCollectionTableController.d.ts +3 -0
  37. package/dist/components/index.d.ts +6 -3
  38. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  39. package/dist/{components/FireCMSAppBar.d.ts → core/DefaultAppBar.d.ts} +5 -8
  40. package/dist/core/DefaultDrawer.d.ts +19 -0
  41. package/dist/core/DrawerNavigationItem.d.ts +9 -0
  42. package/dist/core/EntityEditView.d.ts +36 -0
  43. package/dist/core/NavigationRoutes.d.ts +2 -2
  44. package/dist/core/index.d.ts +3 -4
  45. package/dist/form/PropertiesForm.d.ts +8 -0
  46. package/dist/form/components/ErrorFocus.d.ts +1 -1
  47. package/dist/form/components/FieldHelperText.d.ts +3 -3
  48. package/dist/form/components/StorageItemPreview.d.ts +2 -3
  49. package/dist/form/components/StorageUploadProgress.d.ts +1 -1
  50. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  51. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  52. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -4
  53. package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
  54. package/dist/form/index.d.ts +0 -2
  55. package/dist/form/validation.d.ts +1 -1
  56. package/dist/hooks/data/delete.d.ts +2 -2
  57. package/dist/hooks/data/save.d.ts +2 -3
  58. package/dist/hooks/data/useDataSource.d.ts +2 -2
  59. package/dist/hooks/data/useEntityFetch.d.ts +3 -3
  60. package/dist/hooks/index.d.ts +2 -0
  61. package/dist/hooks/useBuildNavigationController.d.ts +6 -4
  62. package/dist/hooks/useProjectLog.d.ts +6 -2
  63. package/dist/hooks/useStorageSource.d.ts +2 -2
  64. package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
  65. package/dist/index.d.ts +1 -0
  66. package/dist/index.es.js +10512 -9997
  67. package/dist/index.es.js.map +1 -1
  68. package/dist/index.umd.js +5 -5
  69. package/dist/index.umd.js.map +1 -1
  70. package/dist/internal/useBuildDataSource.d.ts +1 -16
  71. package/dist/preview/PropertyPreview.d.ts +1 -1
  72. package/dist/preview/PropertyPreviewProps.d.ts +1 -4
  73. package/dist/preview/components/BooleanPreview.d.ts +5 -1
  74. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  75. package/dist/preview/components/ReferencePreview.d.ts +3 -8
  76. package/dist/types/analytics.d.ts +1 -1
  77. package/dist/types/auth.d.ts +37 -1
  78. package/dist/types/collections.d.ts +44 -6
  79. package/dist/types/datasource.d.ts +21 -14
  80. package/dist/types/entities.d.ts +5 -1
  81. package/dist/types/entity_actions.d.ts +14 -0
  82. package/dist/types/entity_callbacks.d.ts +2 -2
  83. package/dist/types/entity_overrides.d.ts +6 -0
  84. package/dist/types/fields.d.ts +31 -30
  85. package/dist/types/index.d.ts +2 -1
  86. package/dist/types/navigation.d.ts +15 -14
  87. package/dist/types/permissions.d.ts +5 -1
  88. package/dist/types/plugins.d.ts +22 -22
  89. package/dist/types/properties.d.ts +13 -5
  90. package/dist/types/property_config.d.ts +2 -2
  91. package/dist/types/roles.d.ts +31 -0
  92. package/dist/types/storage.d.ts +11 -3
  93. package/dist/types/user.d.ts +5 -0
  94. package/dist/util/collections.d.ts +9 -1
  95. package/dist/util/entities.d.ts +1 -1
  96. package/dist/util/icon_synonyms.d.ts +1 -97
  97. package/dist/util/icons.d.ts +8 -2
  98. package/dist/util/navigation_utils.d.ts +2 -2
  99. package/dist/util/objects.d.ts +1 -1
  100. package/dist/util/permissions.d.ts +4 -4
  101. package/dist/util/references.d.ts +4 -2
  102. package/dist/util/resolutions.d.ts +14 -14
  103. package/dist/util/storage.d.ts +23 -2
  104. package/dist/util/useStorageUploadController.d.ts +1 -1
  105. package/dist/util/useTraceUpdate.d.ts +1 -0
  106. package/package.json +139 -119
  107. package/src/app/AppBar.tsx +18 -0
  108. package/src/app/Drawer.tsx +25 -0
  109. package/src/app/Scaffold.tsx +249 -0
  110. package/src/app/index.ts +4 -0
  111. package/src/app/useApp.tsx +32 -0
  112. package/src/components/ClearFilterSortButton.tsx +41 -0
  113. package/src/components/DeleteEntityDialog.tsx +4 -4
  114. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +4 -4
  115. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +276 -279
  116. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +9 -5
  117. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +48 -45
  118. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  119. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +18 -17
  120. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +5 -5
  121. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +29 -34
  122. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +16 -12
  123. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +4 -5
  124. package/src/components/EntityCollectionView/EntityCollectionView.tsx +73 -72
  125. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
  126. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  127. package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
  128. package/src/components/EntityPreview.tsx +209 -70
  129. package/src/components/EntityView.tsx +84 -0
  130. package/src/components/FieldCaption.tsx +14 -0
  131. package/src/components/HomePage/DefaultHomePage.tsx +15 -11
  132. package/src/components/HomePage/NavigationCard.tsx +69 -0
  133. package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
  134. package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
  135. package/src/components/HomePage/index.tsx +3 -1
  136. package/src/components/PropertyIdCopyTooltipContent.tsx +2 -3
  137. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +4 -4
  138. package/src/components/ReferenceWidget.tsx +22 -12
  139. package/src/components/SearchIconsView.tsx +5 -5
  140. package/src/components/SelectableTable/SelectableTable.tsx +5 -3
  141. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  142. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
  143. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +38 -24
  144. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
  145. package/src/components/VirtualTable/VirtualTable.tsx +38 -29
  146. package/src/components/VirtualTable/VirtualTableHeader.tsx +4 -4
  147. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  148. package/src/components/VirtualTable/VirtualTableProps.tsx +7 -7
  149. package/src/components/VirtualTable/VirtualTableRow.tsx +4 -5
  150. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
  151. package/src/components/VirtualTable/types.tsx +2 -3
  152. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +11 -7
  153. package/src/components/common/index.ts +1 -0
  154. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  155. package/src/components/common/types.tsx +4 -6
  156. package/src/components/common/useColumnsIds.tsx +10 -2
  157. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +12 -1
  158. package/src/components/common/useTableSearchHelper.ts +39 -9
  159. package/src/components/index.tsx +6 -3
  160. package/src/contexts/AuthControllerContext.tsx +1 -1
  161. package/src/{components/FireCMSAppBar.tsx → core/DefaultAppBar.tsx} +51 -34
  162. package/src/core/DefaultDrawer.tsx +177 -0
  163. package/src/core/DrawerNavigationItem.tsx +62 -0
  164. package/src/core/EntityEditView.tsx +1101 -0
  165. package/src/core/EntitySidePanel.tsx +3 -4
  166. package/src/core/FireCMS.tsx +54 -43
  167. package/src/core/NavigationRoutes.tsx +14 -7
  168. package/src/core/field_configs.tsx +2 -3
  169. package/src/core/index.tsx +3 -4
  170. package/src/form/PropertiesForm.tsx +81 -0
  171. package/src/form/PropertyFieldBinding.tsx +29 -7
  172. package/src/form/components/FieldHelperText.tsx +3 -3
  173. package/src/form/components/StorageItemPreview.tsx +5 -7
  174. package/src/form/components/StorageUploadProgress.tsx +9 -8
  175. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +10 -12
  176. package/src/form/field_bindings/BlockFieldBinding.tsx +2 -2
  177. package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -1
  178. package/src/form/field_bindings/KeyValueFieldBinding.tsx +19 -19
  179. package/src/form/field_bindings/MapFieldBinding.tsx +25 -17
  180. package/src/form/field_bindings/MarkdownFieldBinding.tsx +2 -2
  181. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -9
  182. package/src/form/field_bindings/ReferenceFieldBinding.tsx +16 -13
  183. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -3
  184. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +14 -35
  185. package/src/form/field_bindings/TextFieldBinding.tsx +7 -5
  186. package/src/form/index.tsx +4 -4
  187. package/src/form/validation.ts +4 -21
  188. package/src/hooks/data/delete.ts +3 -3
  189. package/src/hooks/data/save.ts +4 -2
  190. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  191. package/src/hooks/data/useDataSource.tsx +8 -3
  192. package/src/hooks/data/useEntityFetch.tsx +4 -4
  193. package/src/hooks/index.tsx +3 -0
  194. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  195. package/src/hooks/useBuildModeController.tsx +11 -5
  196. package/src/hooks/useBuildNavigationController.tsx +200 -83
  197. package/src/hooks/useProjectLog.tsx +17 -7
  198. package/src/hooks/useReferenceDialog.tsx +2 -2
  199. package/src/hooks/useResolvedNavigationFrom.tsx +1 -1
  200. package/src/hooks/useStorageSource.tsx +7 -2
  201. package/src/hooks/useValidateAuthenticator.tsx +115 -0
  202. package/src/index.ts +1 -0
  203. package/src/internal/useBuildDataSource.ts +54 -47
  204. package/src/internal/useBuildSideEntityController.tsx +88 -21
  205. package/src/preview/PropertyPreview.tsx +5 -15
  206. package/src/preview/PropertyPreviewProps.tsx +1 -11
  207. package/src/preview/components/BooleanPreview.tsx +19 -4
  208. package/src/preview/components/EnumValuesChip.tsx +2 -2
  209. package/src/preview/components/ReferencePreview.tsx +72 -165
  210. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -1
  211. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -1
  212. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -1
  213. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -1
  214. package/src/preview/property_previews/ArrayOneOfPreview.tsx +2 -3
  215. package/src/preview/property_previews/ArrayPropertyPreview.tsx +2 -3
  216. package/src/preview/property_previews/MapPropertyPreview.tsx +5 -5
  217. package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
  218. package/src/types/analytics.ts +1 -0
  219. package/src/types/auth.tsx +50 -1
  220. package/src/types/collections.ts +51 -6
  221. package/src/types/customization_controller.tsx +0 -1
  222. package/src/types/datasource.ts +24 -17
  223. package/src/types/entities.ts +9 -1
  224. package/src/types/entity_actions.tsx +17 -0
  225. package/src/types/entity_callbacks.ts +2 -2
  226. package/src/types/entity_overrides.tsx +7 -0
  227. package/src/types/fields.tsx +33 -33
  228. package/src/types/firecms.tsx +0 -1
  229. package/src/types/index.ts +2 -1
  230. package/src/types/navigation.ts +17 -17
  231. package/src/types/permissions.ts +6 -1
  232. package/src/types/plugins.tsx +28 -30
  233. package/src/types/properties.ts +19 -7
  234. package/src/types/property_config.tsx +2 -2
  235. package/src/types/roles.ts +41 -0
  236. package/src/types/side_entity_controller.tsx +1 -0
  237. package/src/types/storage.ts +12 -3
  238. package/src/types/user.ts +7 -0
  239. package/src/util/collections.ts +22 -0
  240. package/src/util/entities.ts +2 -1
  241. package/src/util/enums.ts +1 -1
  242. package/src/util/icon_list.ts +2 -2
  243. package/src/util/icon_synonyms.ts +3 -99
  244. package/src/util/icons.tsx +11 -3
  245. package/src/util/navigation_utils.ts +6 -6
  246. package/src/util/objects.ts +8 -21
  247. package/src/util/permissions.ts +12 -8
  248. package/src/util/references.ts +36 -5
  249. package/src/util/resolutions.ts +32 -31
  250. package/src/util/storage.ts +75 -21
  251. package/src/util/strings.ts +2 -2
  252. package/src/util/useStorageUploadController.tsx +21 -3
  253. package/src/util/useTraceUpdate.tsx +2 -1
  254. package/dist/components/VirtualTable/common.d.ts +0 -2
  255. package/dist/core/Drawer.d.ts +0 -23
  256. package/dist/core/EntityView.d.ts +0 -22
  257. package/dist/core/Scaffold.d.ts +0 -55
  258. package/dist/core/SideEntityView.d.ts +0 -7
  259. package/dist/form/EntityForm.d.ts +0 -77
  260. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  261. package/dist/internal/useLocaleConfig.d.ts +0 -1
  262. package/dist/types/appcheck.d.ts +0 -26
  263. package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
  264. package/src/core/Drawer.tsx +0 -164
  265. package/src/core/EntityView.tsx +0 -578
  266. package/src/core/Scaffold.tsx +0 -281
  267. package/src/core/SideEntityView.tsx +0 -38
  268. package/src/form/EntityForm.tsx +0 -720
  269. package/src/internal/useBuildCustomizationController.tsx +0 -5
  270. package/src/internal/useLocaleConfig.tsx +0 -18
  271. package/src/types/appcheck.ts +0 -29
@@ -1,5 +1,4 @@
1
1
  import React from "react";
2
- import { CMSView, EntityCollection } from "../types";
3
2
  import { hashString } from "./hash";
4
3
  import { coolIconKeys, Icon, iconKeys } from "@firecms/ui";
5
4
  import { slugify } from "./strings";
@@ -14,8 +13,17 @@ export function getIcon(iconKey?: string, className?: string): React.ReactElemen
14
13
  return iconKey in iconKeysMap ? <Icon iconKey={iconKey} size={"medium"} className={className}/> : undefined;
15
14
  }
16
15
 
16
+ export type IconViewProps = {
17
+ path: string;
18
+ name: string;
19
+ singularName?: string;
20
+ group?: string;
21
+ icon?: string;
22
+ }
23
+
17
24
  export const IconForView = React.memo(
18
- function IconForView({ collectionOrView, className }: { collectionOrView: EntityCollection | CMSView, className?: string }): React.ReactElement {
25
+ function IconForView({ collectionOrView, className }: { collectionOrView?: IconViewProps, className?: string }): React.ReactElement {
26
+ if (!collectionOrView) return <></>;
19
27
  const icon = getIcon(collectionOrView.icon, className);
20
28
  if (collectionOrView?.icon && icon)
21
29
  return icon;
@@ -39,7 +47,7 @@ export const IconForView = React.memo(
39
47
 
40
48
  return <Icon iconKey={key} size={"medium"} className={className}/>;
41
49
  }, (prevProps, nextProps) => {
42
- return equal(prevProps.collectionOrView.icon, nextProps.collectionOrView.icon);
50
+ return equal(prevProps.collectionOrView?.icon, nextProps.collectionOrView?.icon);
43
51
  });
44
52
 
45
53
  const iconKeysMap: Record<string, string> = iconKeys.reduce((acc: Record<string, string>, key) => {
@@ -60,14 +60,14 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
60
60
  /**
61
61
  * Find the corresponding view at any depth for a given path.
62
62
  * Note that path or segments of the paths can be collection aliases.
63
- * @param pathOrAlias
63
+ * @param pathOrId
64
64
  * @param collections
65
65
  */
66
- export function getCollectionByPathOrId(pathOrAlias: string, collections: EntityCollection[]): EntityCollection | undefined {
66
+ export function getCollectionByPathOrId(pathOrId: string, collections: EntityCollection[]): EntityCollection | undefined {
67
67
 
68
- const subpaths = removeInitialAndTrailingSlashes(pathOrAlias).split("/");
68
+ const subpaths = removeInitialAndTrailingSlashes(pathOrId).split("/");
69
69
  if (subpaths.length % 2 === 0) {
70
- throw Error(`getCollectionByPathOrAlias: Collection paths must have an odd number of segments: ${pathOrAlias}`);
70
+ throw Error(`getCollectionByPathOrId: Collection paths must have an odd number of segments: ${pathOrId}`);
71
71
  }
72
72
 
73
73
  const subpathCombinations = getCollectionPathsCombinations(subpaths);
@@ -80,10 +80,10 @@ export function getCollectionByPathOrId(pathOrAlias: string, collections: Entity
80
80
 
81
81
  if (navigationEntry) {
82
82
 
83
- if (subpathCombination === pathOrAlias) {
83
+ if (subpathCombination === pathOrId) {
84
84
  result = navigationEntry;
85
85
  } else if (navigationEntry.subcollections) {
86
- const newPath = pathOrAlias.replace(subpathCombination, "").split("/").slice(2).join("/");
86
+ const newPath = pathOrId.replace(subpathCombination, "").split("/").slice(2).join("/");
87
87
  if (newPath.length > 0)
88
88
  result = getCollectionByPathOrId(newPath, navigationEntry.subcollections);
89
89
  }
@@ -12,38 +12,25 @@ export function isObject(item: any) {
12
12
  return item && typeof item === "object" && !Array.isArray(item);
13
13
  }
14
14
 
15
- export function mergeDeep<T extends object>(target: T, source: any): T {
15
+ export function mergeDeep<T extends Record<any, any>, U extends Record<any, any>>(target: T, source: U): T & U {
16
16
  const targetIsObject = isObject(target);
17
- const output: T = targetIsObject ? { ...target } : target;
17
+ const output = targetIsObject ? { ...target } : target;
18
18
  if (targetIsObject && isObject(source)) {
19
19
  Object.keys(source).forEach(key => {
20
- if (isObject(source[key])) {
20
+ const sourceElement = source[key];
21
+ if (isObject(sourceElement)) {
21
22
  if (!(key in target))
22
- Object.assign(output, { [key]: source[key] });
23
+ Object.assign(output, { [key]: sourceElement });
23
24
  else
24
- (output as any)[key] = mergeDeep((target as any)[key], source[key]);
25
+ (output as any)[key] = mergeDeep((target as any)[key], sourceElement);
25
26
  } else {
26
- Object.assign(output, { [key]: source[key] });
27
+ Object.assign(output, { [key]: sourceElement });
27
28
  }
28
29
  });
29
30
  }
30
- return output;
31
+ return output as T;
31
32
  }
32
33
 
33
- // export function getValueInPath(o: object | undefined, path: string): any {
34
- // if (!o) return undefined;
35
- // if (typeof o === "object") {
36
- // if (path in o) {
37
- // return (o as any)[path];
38
- // }
39
- // if (path.includes(".")) {
40
- // const pathSegments = path.split(".");
41
- // return getValueInPath((o as any)[pathSegments[0]], pathSegments.slice(1).join("."))
42
- // }
43
- // }
44
- // return undefined;
45
- // }
46
-
47
34
  export function getValueInPath(o: object | undefined, path: string): any {
48
35
  if (!o) return undefined;
49
36
  if (typeof o === "object") {
@@ -1,4 +1,5 @@
1
1
  import { AuthController, Entity, EntityCollection, Permissions, User } from "../types";
2
+ import { fullPathToCollectionSegments } from "./paths";
2
3
 
3
4
  const DEFAULT_PERMISSIONS = {
4
5
  read: true,
@@ -10,8 +11,8 @@ const DEFAULT_PERMISSIONS = {
10
11
  export function resolvePermissions<M extends Record<string, any>, UserType extends User>
11
12
  (collection: EntityCollection<M>,
12
13
  authController: AuthController<UserType>,
13
- pathSegments: string[],
14
- entity: Entity<M> | null): Permissions {
14
+ path: string,
15
+ entity: Entity<M> | null): Permissions | undefined {
15
16
 
16
17
  const permission = collection.permissions;
17
18
  if (permission === undefined) {
@@ -19,8 +20,10 @@ export function resolvePermissions<M extends Record<string, any>, UserType exten
19
20
  } else if (typeof permission === "object") {
20
21
  return permission as Permissions;
21
22
  } else if (typeof permission === "function") {
23
+ const pathSegments = fullPathToCollectionSegments(path);
22
24
  return permission({
23
25
  entity,
26
+ path,
24
27
  user: authController.user,
25
28
  authController,
26
29
  collection,
@@ -35,27 +38,28 @@ export function canEditEntity<M extends Record<string, any>, UserType extends Us
35
38
  (
36
39
  collection: EntityCollection<M>,
37
40
  authController: AuthController<UserType>,
38
- paths: string[],
41
+ path: string,
39
42
  entity: Entity<M> | null): boolean {
40
- return resolvePermissions(collection, authController, paths, entity).edit ?? DEFAULT_PERMISSIONS.edit;
43
+ return resolvePermissions(collection, authController, path, entity)?.edit ?? DEFAULT_PERMISSIONS.edit;
41
44
  }
42
45
 
43
46
  export function canCreateEntity<M extends Record<string, any>, UserType extends User>
44
47
  (
45
48
  collection: EntityCollection<M>,
46
49
  authController: AuthController<UserType>,
47
- paths: string[],
50
+ path: string,
48
51
  entity: Entity<M> | null): boolean {
49
- return resolvePermissions(collection, authController, paths, entity).create ?? DEFAULT_PERMISSIONS.create;
52
+ if (collection.collectionGroup) return false;
53
+ return resolvePermissions(collection, authController, path, entity)?.create ?? DEFAULT_PERMISSIONS.create;
50
54
  }
51
55
 
52
56
  export function canDeleteEntity<M extends Record<string, any>, UserType extends User>
53
57
  (
54
58
  collection: EntityCollection<M>,
55
59
  authController: AuthController<UserType>,
56
- paths: string[],
60
+ path: string,
57
61
  entity: Entity<M> | null): boolean {
58
- return resolvePermissions(collection, authController, paths, entity).delete ?? DEFAULT_PERMISSIONS.delete;
62
+ return resolvePermissions(collection, authController, path, entity)?.delete ?? DEFAULT_PERMISSIONS.delete;
59
63
  }
60
64
 
61
65
  // export function resolveCollectionsPermissions(roles: Role[]): Record<string, Permissions> {
@@ -1,11 +1,12 @@
1
- import { EntityCollection, PropertyConfig } from "../types";
1
+ import { EntityCollection, PropertyConfig, ResolvedEntityCollection } from "../types";
2
2
  import { isReferenceProperty } from "./property_utils";
3
3
  import { isPropertyBuilder } from "./entities";
4
+ import { getFieldConfig } from "../core";
4
5
 
5
- export function getReferencePreviewKeys(targetCollection: EntityCollection<any>,
6
- fields: Record<string, PropertyConfig>,
7
- previewProperties?: string[],
8
- limit = 3) {
6
+ export function getEntityPreviewKeys(targetCollection: EntityCollection<any>,
7
+ fields: Record<string, PropertyConfig>,
8
+ previewProperties?: string[],
9
+ limit = 3) {
9
10
  const allProperties = Object.keys(targetCollection.properties);
10
11
  let listProperties = previewProperties?.filter(p => allProperties.includes(p as string));
11
12
  if (listProperties && listProperties.length > 0) {
@@ -18,3 +19,33 @@ export function getReferencePreviewKeys(targetCollection: EntityCollection<any>,
18
19
  }).slice(0, limit);
19
20
  }
20
21
  }
22
+
23
+ export function getEntityTitlePropertyKey<M extends Record<string, any>>(collection: EntityCollection<M>, propertyConfigs: Record<string, PropertyConfig<any>>): string | undefined {
24
+ if (collection.titleProperty) {
25
+ return collection.titleProperty as string;
26
+ }
27
+ // find first text field property
28
+ for (const key in collection.properties) {
29
+ const property = collection.properties[key];
30
+ if (!isPropertyBuilder(property)) {
31
+ const field = getFieldConfig(property, propertyConfigs);
32
+ if (field?.key === "text_field") {
33
+ return key;
34
+ }
35
+ }
36
+ }
37
+ return undefined;
38
+ }
39
+
40
+ export function getEntityImagePreviewPropertyKey<M extends object>(collection: ResolvedEntityCollection<M>): string | undefined {
41
+
42
+ // find first text field property
43
+ for (const key in collection.properties) {
44
+ const property = collection.properties[key];
45
+ if (property.dataType === "string" && property.storage?.acceptedFiles?.includes("image/*")) {
46
+ return key;
47
+ }
48
+
49
+ }
50
+ return undefined;
51
+ }
@@ -26,7 +26,7 @@ import { getDefaultValuesFor, isPropertyBuilder } from "./entities";
26
26
  import { DEFAULT_ONE_OF_TYPE } from "./common";
27
27
  import { getIn } from "@firecms/formex";
28
28
  import { enumToObjectEntries } from "./enums";
29
- import { isDefaultFieldConfigId } from "../core";
29
+ import { isDefaultFieldConfigId } from "../core/field_configs";
30
30
 
31
31
  // import util from "util";
32
32
 
@@ -38,7 +38,8 @@ export const resolveCollection = <M extends Record<string, any>, >
38
38
  values,
39
39
  previousValues,
40
40
  userConfigPersistence,
41
- fields
41
+ fields,
42
+ ignoreMissingFields = false
42
43
  }: {
43
44
  collection: EntityCollection<M> | ResolvedEntityCollection<M>;
44
45
  path: string,
@@ -47,6 +48,7 @@ export const resolveCollection = <M extends Record<string, any>, >
47
48
  previousValues?: Partial<EntityValues<M>>,
48
49
  userConfigPersistence?: UserConfigurationPersistence;
49
50
  fields?: Record<string, PropertyConfig>;
51
+ ignoreMissingFields?: boolean;
50
52
  }): ResolvedEntityCollection<M> => {
51
53
 
52
54
  const collectionOverride = userConfigPersistence?.getCollectionConfig<M>(path);
@@ -58,16 +60,15 @@ export const resolveCollection = <M extends Record<string, any>, >
58
60
 
59
61
  const resolvedProperties = Object.entries(collection.properties)
60
62
  .map(([key, propertyOrBuilder]) => {
61
- const propertyValue = usedValues ? getIn(usedValues, key) : undefined;
62
63
  const childResolvedProperty = resolveProperty({
63
64
  propertyKey: key,
64
65
  propertyOrBuilder: propertyOrBuilder as PropertyOrBuilder | ResolvedProperty,
65
66
  values: usedValues,
66
67
  previousValues: usedPreviousValues,
67
68
  path,
68
- propertyValue,
69
69
  entityId,
70
- fields
70
+ fields,
71
+ ignoreMissingFields
71
72
  });
72
73
  if (!childResolvedProperty) return {};
73
74
  return ({
@@ -95,17 +96,15 @@ export const resolveCollection = <M extends Record<string, any>, >
95
96
  * Resolve property builders, enums and arrays.
96
97
  * @param propertyOrBuilder
97
98
  * @param propertyValue
98
- * @param values
99
99
  */
100
100
  export function resolveProperty<T extends CMSType = CMSType, M extends Record<string, any> = any>({
101
101
  propertyOrBuilder,
102
- propertyValue,
103
102
  fromBuilder = false,
103
+ ignoreMissingFields = false,
104
104
  ...props
105
105
  }: {
106
106
  propertyKey?: string,
107
107
  propertyOrBuilder: PropertyOrBuilder<T, M> | ResolvedProperty<T>,
108
- propertyValue?: unknown,
109
108
  values?: Partial<M>,
110
109
  previousValues?: Partial<M>,
111
110
  path?: string,
@@ -113,6 +112,7 @@ export function resolveProperty<T extends CMSType = CMSType, M extends Record<st
113
112
  index?: number,
114
113
  fromBuilder?: boolean;
115
114
  fields?: Record<string, PropertyConfig<any>>;
115
+ ignoreMissingFields?: boolean;
116
116
  }): ResolvedProperty<T> | null {
117
117
 
118
118
  if (typeof propertyOrBuilder === "object" && "resolved" in propertyOrBuilder) {
@@ -128,10 +128,11 @@ export function resolveProperty<T extends CMSType = CMSType, M extends Record<st
128
128
  if (!path)
129
129
  throw Error("Trying to resolve a property builder without specifying the entity path");
130
130
 
131
+ const usedPropertyValue = props.propertyKey ? getIn(props.values, props.propertyKey) : undefined;
131
132
  const result: Property<T> | null = propertyOrBuilder({
132
133
  ...props,
133
134
  path,
134
- propertyValue,
135
+ propertyValue: usedPropertyValue,
135
136
  values: props.values ?? {},
136
137
  previousValues: props.previousValues ?? props.values ?? {}
137
138
  });
@@ -142,17 +143,17 @@ export function resolveProperty<T extends CMSType = CMSType, M extends Record<st
142
143
 
143
144
  resolvedProperty = resolveProperty({
144
145
  ...props,
145
- propertyValue,
146
146
  propertyOrBuilder: result,
147
- fromBuilder: true
147
+ fromBuilder: true,
148
+ ignoreMissingFields
148
149
  });
149
150
  } else {
150
151
  const property = propertyOrBuilder as Property<T>;
151
152
  if (property.dataType === "map" && property.properties) {
152
153
  const properties = resolveProperties({
154
+ ignoreMissingFields,
153
155
  ...props,
154
156
  properties: property.properties,
155
- propertyValue
156
157
  });
157
158
  resolvedProperty = {
158
159
  ...property,
@@ -163,8 +164,8 @@ export function resolveProperty<T extends CMSType = CMSType, M extends Record<st
163
164
  } else if (property.dataType === "array") {
164
165
  resolvedProperty = resolveArrayProperty({
165
166
  property,
166
- propertyValue,
167
167
  fromBuilder,
168
+ ignoreMissingFields,
168
169
  ...props
169
170
  }) as ResolvedProperty<any>;
170
171
  } else if ((property.dataType === "string" || property.dataType === "number") && property.enumValues) {
@@ -182,12 +183,12 @@ export function resolveProperty<T extends CMSType = CMSType, M extends Record<st
182
183
 
183
184
  if (resolvedProperty.propertyConfig && !isDefaultFieldConfigId(resolvedProperty.propertyConfig)) {
184
185
  const cmsFields = props.fields;
185
- if (!cmsFields) {
186
- throw Error(`Trying to resolve a property with key ${resolvedProperty.propertyConfig} that inherits from a custom property config but no custom property configs were provided. Use the property \`propertyConfigs\` in your app config to provide them`);
186
+ if (!cmsFields && !ignoreMissingFields) {
187
+ throw Error(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property configs were provided. Use the property 'propertyConfigs' in your app config to provide them`);
187
188
  }
188
- const customField: PropertyConfig<any> = cmsFields[resolvedProperty.propertyConfig];
189
+ const customField: PropertyConfig | undefined = cmsFields?.[resolvedProperty.propertyConfig];
189
190
  if (!customField) {
190
- console.warn(`Trying to resolve a property with key ${resolvedProperty.propertyConfig} that inherits from a custom property config but no custom property config with that key was found. Check the \`propertyConfigs\` in your app config`)
191
+ console.warn(`Trying to resolve a property with key '${resolvedProperty.propertyConfig}' that inherits from a custom property config but no custom property config with that key was found. Check the 'propertyConfigs' in your app config`)
191
192
  console.warn("Available property configs", cmsFields);
192
193
  return null;
193
194
  }
@@ -198,7 +199,7 @@ export function resolveProperty<T extends CMSType = CMSType, M extends Record<st
198
199
  }
199
200
  const customFieldProperty = resolveProperty<any>({
200
201
  propertyOrBuilder: configPropertyOrBuilder,
201
- propertyValue,
202
+ ignoreMissingFields,
202
203
  ...props
203
204
  });
204
205
  if (customFieldProperty) {
@@ -219,12 +220,11 @@ export function resolveProperty<T extends CMSType = CMSType, M extends Record<st
219
220
  export function resolveArrayProperty<T extends any[], M>({
220
221
  propertyKey,
221
222
  property,
222
- propertyValue,
223
+ ignoreMissingFields = false,
223
224
  ...props
224
225
  }: {
225
226
  propertyKey?: string,
226
227
  property: ArrayProperty<T> | ResolvedArrayProperty<T>,
227
- propertyValue: any,
228
228
  values?: Partial<M>,
229
229
  previousValues?: Partial<M>,
230
230
  path?: string,
@@ -232,7 +232,9 @@ export function resolveArrayProperty<T extends any[], M>({
232
232
  index?: number,
233
233
  fromBuilder?: boolean;
234
234
  fields?: Record<string, PropertyConfig>;
235
+ ignoreMissingFields?: boolean;
235
236
  }): ResolvedArrayProperty {
237
+ const propertyValue = propertyKey ? getIn(props.values, propertyKey) : undefined;
236
238
 
237
239
  if (property.of) {
238
240
  if (Array.isArray(property.of)) {
@@ -244,7 +246,7 @@ export function resolveArrayProperty<T extends any[], M>({
244
246
  return resolveProperty({
245
247
  propertyKey: `${propertyKey}.${index}`,
246
248
  propertyOrBuilder: p as Property<any>,
247
- propertyValue: Array.isArray(propertyValue) ? propertyValue[index] : undefined,
249
+ ignoreMissingFields,
248
250
  ...props,
249
251
  index
250
252
  });
@@ -256,7 +258,7 @@ export function resolveArrayProperty<T extends any[], M>({
256
258
  ? propertyValue.map((v: any, index: number) => resolveProperty({
257
259
  propertyKey: `${propertyKey}.${index}`,
258
260
  propertyOrBuilder: of,
259
- propertyValue: v,
261
+ ignoreMissingFields,
260
262
  ...props,
261
263
  index
262
264
  })).filter(e => Boolean(e)) as ResolvedProperty[]
@@ -264,10 +266,10 @@ export function resolveArrayProperty<T extends any[], M>({
264
266
  const ofProperty = resolveProperty({
265
267
  propertyKey: `${propertyKey}`,
266
268
  propertyOrBuilder: of,
267
- propertyValue: undefined,
269
+ ignoreMissingFields,
268
270
  ...props
269
271
  });
270
- if (!ofProperty)
272
+ if (!ofProperty && !ignoreMissingFields)
271
273
  throw Error("When using a property builder as the 'of' prop of an ArrayProperty, you must return a valid child property")
272
274
  return {
273
275
  ...property,
@@ -287,14 +289,14 @@ export function resolveArrayProperty<T extends any[], M>({
287
289
  return resolveProperty({
288
290
  propertyKey: `${propertyKey}.${index}`,
289
291
  propertyOrBuilder: childProperty,
290
- propertyValue,
292
+ ignoreMissingFields,
291
293
  ...props
292
294
  });
293
295
  }).filter(e => Boolean(e)) as ResolvedProperty[]
294
296
  : [];
295
297
  const properties = resolveProperties<any>({
296
298
  properties: property.oneOf.properties,
297
- propertyValue: undefined,
299
+ ignoreMissingFields,
298
300
  ...props
299
301
  });
300
302
  return {
@@ -326,11 +328,10 @@ export function resolveArrayProperty<T extends any[], M>({
326
328
  */
327
329
  export function resolveProperties<M extends Record<string, any>>({
328
330
  properties,
329
- propertyValue,
331
+ ignoreMissingFields,
330
332
  ...props
331
333
  }: {
332
334
  properties: PropertiesOrBuilders<M>,
333
- propertyValue: unknown,
334
335
  values?: Partial<M>,
335
336
  previousValues?: Partial<M>,
336
337
  path?: string,
@@ -338,14 +339,14 @@ export function resolveProperties<M extends Record<string, any>>({
338
339
  index?: number,
339
340
  fromBuilder?: boolean;
340
341
  fields?: Record<string, PropertyConfig>;
342
+ ignoreMissingFields?: boolean;
341
343
  }): ResolvedProperties<M> {
342
344
  return Object.entries<PropertyOrBuilder>(properties as Record<string, PropertyOrBuilder>)
343
345
  .map(([key, property]) => {
344
- const thisPropertyValue = propertyValue && typeof propertyValue === "object" ? getValueInPath(propertyValue, key) : undefined;
345
346
  const childResolvedProperty = resolveProperty({
346
347
  propertyKey: key,
347
348
  propertyOrBuilder: property,
348
- propertyValue: thisPropertyValue,
349
+ ignoreMissingFields,
349
350
  ...props
350
351
  });
351
352
  if (!childResolvedProperty) return {};
@@ -367,7 +368,7 @@ export function resolvePropertyEnum(property: StringProperty | NumberProperty, f
367
368
  return {
368
369
  ...property,
369
370
  resolved: true,
370
- enumValues: enumToObjectEntries(property.enumValues)?.filter((value) => value && value.id && value.label) ?? [],
371
+ enumValues: enumToObjectEntries(property.enumValues)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? [],
371
372
  fromBuilder: fromBuilder ?? false
372
373
  }
373
374
  }
@@ -7,15 +7,28 @@ import {
7
7
  } from "../types";
8
8
  import { randomString } from "./strings";
9
9
 
10
+ interface ResolveFilenameStringParams<M extends object> {
11
+ input: string | ((context: UploadedFileContext) => (Promise<string> | string));
12
+ storage: StorageConfig;
13
+ values: EntityValues<M>;
14
+ entityId: string;
15
+ path?: string;
16
+ property: ResolvedStringProperty | ResolvedArrayProperty<string[]>;
17
+ file: File;
18
+ propertyKey: string;
19
+ }
20
+
10
21
  export async function resolveFilenameString<M extends object>(
11
- input: string | ((context: UploadedFileContext) => Promise<string> | string),
12
- storage: StorageConfig,
13
- values: EntityValues<M>,
14
- entityId: string,
15
- path: string,
16
- property: ResolvedStringProperty | ResolvedArrayProperty<string[]>,
17
- file: File,
18
- propertyKey: string): Promise<string> {
22
+ {
23
+ input,
24
+ storage,
25
+ values,
26
+ entityId,
27
+ path,
28
+ property,
29
+ file,
30
+ propertyKey
31
+ }: ResolveFilenameStringParams<M>): Promise<string> {
19
32
  let result;
20
33
  if (typeof input === "function") {
21
34
  result = await input({
@@ -30,7 +43,13 @@ export async function resolveFilenameString<M extends object>(
30
43
  if (!result)
31
44
  console.warn("Storage callback returned empty result. Using default name value")
32
45
  } else {
33
- result = replacePlaceholders(file, input, entityId, propertyKey, path);
46
+ result = replacePlaceholders({
47
+ file,
48
+ input,
49
+ entityId,
50
+ propertyKey,
51
+ path
52
+ });
34
53
  }
35
54
 
36
55
  if (!result)
@@ -39,15 +58,28 @@ export async function resolveFilenameString<M extends object>(
39
58
  return result;
40
59
  }
41
60
 
61
+ interface ResolveStoragePathStringParams<M extends object> {
62
+ input: string | ((context: UploadedFileContext) => string);
63
+ storage: StorageConfig;
64
+ values: EntityValues<M>;
65
+ entityId: string;
66
+ path?: string;
67
+ property: ResolvedStringProperty | ResolvedArrayProperty<string[]>;
68
+ file: File;
69
+ propertyKey: string;
70
+ }
71
+
42
72
  export function resolveStoragePathString<M extends object>(
43
- input: string | ((context: UploadedFileContext) => string),
44
- storage: StorageConfig,
45
- values: EntityValues<M>,
46
- entityId: string,
47
- path: string,
48
- property: ResolvedStringProperty | ResolvedArrayProperty<string[]>,
49
- file: File,
50
- propertyKey: string): string {
73
+ {
74
+ input,
75
+ storage,
76
+ values,
77
+ entityId,
78
+ path,
79
+ property,
80
+ file,
81
+ propertyKey
82
+ }: ResolveStoragePathStringParams<M>): string {
51
83
  let result;
52
84
  if (typeof input === "function") {
53
85
  result = input({
@@ -62,7 +94,13 @@ export function resolveStoragePathString<M extends object>(
62
94
  if (!result)
63
95
  console.warn("Storage callback returned empty result. Using default name value")
64
96
  } else {
65
- result = replacePlaceholders(file, input, entityId, propertyKey, path);
97
+ result = replacePlaceholders({
98
+ file,
99
+ input,
100
+ entityId,
101
+ propertyKey,
102
+ path
103
+ });
66
104
  }
67
105
 
68
106
  if (!result)
@@ -71,14 +109,30 @@ export function resolveStoragePathString<M extends object>(
71
109
  return result;
72
110
  }
73
111
 
74
- function replacePlaceholders(file: File, input: string, entityId: string, propertyKey: string, path: string) {
112
+ interface Placeholders {
113
+ file: File;
114
+ input: string;
115
+ entityId: string;
116
+ propertyKey: string;
117
+ path?: string;
118
+ }
119
+
120
+ function replacePlaceholders({
121
+ file,
122
+ input,
123
+ entityId,
124
+ propertyKey,
125
+ path
126
+ }: Placeholders) {
75
127
  const ext = file.name.split(".").pop();
76
128
  let result = input.replace("{entityId}", entityId)
77
129
  .replace("{propertyKey}", propertyKey)
78
130
  .replace("{rand}", randomString())
79
131
  .replace("{file}", file.name)
80
- .replace("{file.type}", file.type)
81
- .replace("{path}", path);
132
+ .replace("{file.type}", file.type);
133
+ if (path) {
134
+ result = result.replace("{path}", path);
135
+ }
82
136
  if (ext) {
83
137
  result = result.replace("{file.ext}", ext);
84
138
  const name = file.name.replace(`.${ext}`, "");
@@ -56,8 +56,8 @@ export function unslugify(slug?: string): string {
56
56
  const result = slug.replace(/[-_]/g, " ");
57
57
  return result.replace(/\w\S*/g, function (txt) {
58
58
  return txt.charAt(0).toUpperCase() + txt.substr(1);
59
- });
59
+ }).trim();
60
60
  } else {
61
- return slug;
61
+ return slug.trim();
62
62
  }
63
63
  }