@firecms/core 3.0.0-canary.2 → 3.0.0-canary.200

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 (409) hide show
  1. package/README.md +4 -4
  2. package/dist/app/AppBar.d.ts +12 -0
  3. package/dist/app/Drawer.d.ts +16 -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/ArrayContainer.d.ts +23 -6
  8. package/dist/components/CircularProgressCenter.d.ts +1 -1
  9. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  10. package/dist/components/{DeleteConfirmationDialog.d.ts → ConfirmationDialog.d.ts} +1 -1
  11. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +13 -13
  12. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +3 -3
  13. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +22 -6
  14. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +3 -2
  15. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  16. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +3 -1
  17. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  18. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  19. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  20. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +7 -4
  21. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +17 -4
  22. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  23. package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
  24. package/dist/components/EntityCollectionView/utils.d.ts +3 -0
  25. package/dist/components/EntityPreview.d.ts +26 -7
  26. package/dist/components/EntityView.d.ts +11 -0
  27. package/dist/components/ErrorView.d.ts +1 -1
  28. package/dist/components/FieldCaption.d.ts +5 -0
  29. package/dist/components/HomePage/DefaultHomePage.d.ts +1 -1
  30. package/dist/components/HomePage/NavigationCard.d.ts +8 -0
  31. package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +3 -3
  32. package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
  33. package/dist/components/HomePage/index.d.ts +3 -1
  34. package/dist/components/PropertyConfigBadge.d.ts +2 -1
  35. package/dist/components/PropertyIdCopyTooltip.d.ts +8 -0
  36. package/dist/components/ReferenceWidget.d.ts +6 -4
  37. package/dist/components/SelectableTable/SelectableTable.d.ts +13 -3
  38. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  39. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  40. package/dist/components/VirtualTable/VirtualTableProps.d.ts +29 -13
  41. package/dist/components/VirtualTable/types.d.ts +3 -3
  42. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -3
  43. package/dist/components/common/index.d.ts +2 -1
  44. package/dist/components/common/table_height.d.ts +5 -0
  45. package/dist/components/common/types.d.ts +4 -6
  46. package/dist/components/common/useColumnsIds.d.ts +3 -1
  47. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +13 -2
  48. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  49. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  50. package/dist/components/index.d.ts +8 -5
  51. package/dist/contexts/AuthControllerContext.d.ts +1 -1
  52. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  53. package/dist/{components/FireCMSAppBar.d.ts → core/DefaultAppBar.d.ts} +6 -9
  54. package/dist/core/DefaultDrawer.d.ts +19 -0
  55. package/dist/core/DrawerNavigationItem.d.ts +10 -0
  56. package/dist/core/EntityEditView.d.ts +44 -0
  57. package/dist/core/EntityEditViewFormActions.d.ts +2 -0
  58. package/dist/core/FireCMS.d.ts +2 -2
  59. package/dist/core/FireCMSRouter.d.ts +4 -0
  60. package/dist/core/NavigationRoutes.d.ts +2 -3
  61. package/dist/core/SideDialogs.d.ts +4 -2
  62. package/dist/core/index.d.ts +4 -6
  63. package/dist/form/EntityForm.d.ts +45 -69
  64. package/dist/form/EntityFormActions.d.ts +17 -0
  65. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  66. package/dist/form/components/ErrorFocus.d.ts +1 -1
  67. package/dist/form/components/FieldHelperText.d.ts +3 -3
  68. package/dist/form/components/FormEntry.d.ts +6 -0
  69. package/dist/form/components/FormLayout.d.ts +5 -0
  70. package/dist/{components → form/components}/LabelWithIcon.d.ts +1 -1
  71. package/dist/form/components/LabelWithIconAndTooltip.d.ts +15 -0
  72. package/dist/form/components/StorageItemPreview.d.ts +4 -3
  73. package/dist/form/components/StorageUploadProgress.d.ts +1 -1
  74. package/dist/form/components/index.d.ts +4 -1
  75. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  76. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  77. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  78. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  79. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  80. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +11 -0
  81. package/dist/form/field_bindings/{MultiSelectBinding.d.ts → MultiSelectFieldBinding.d.ts} +1 -1
  82. package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
  83. package/dist/form/field_bindings/ReferenceFieldBinding.d.ts +2 -2
  84. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  85. package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
  86. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -4
  87. package/dist/form/field_bindings/SwitchFieldBinding.d.ts +1 -2
  88. package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
  89. package/dist/form/index.d.ts +17 -18
  90. package/dist/form/useClearRestoreValue.d.ts +2 -2
  91. package/dist/form/validation.d.ts +1 -1
  92. package/dist/hooks/data/delete.d.ts +4 -4
  93. package/dist/hooks/data/save.d.ts +4 -5
  94. package/dist/hooks/data/useCollectionFetch.d.ts +1 -1
  95. package/dist/hooks/data/useDataSource.d.ts +2 -2
  96. package/dist/hooks/data/useEntityFetch.d.ts +4 -3
  97. package/dist/hooks/index.d.ts +4 -1
  98. package/dist/hooks/useAuthController.d.ts +1 -1
  99. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  100. package/dist/{core → hooks}/useBuildModeController.d.ts +1 -1
  101. package/dist/hooks/useBuildNavigationController.d.ts +11 -6
  102. package/dist/hooks/useFireCMSContext.d.ts +1 -1
  103. package/dist/hooks/useModeController.d.ts +1 -2
  104. package/dist/hooks/useProjectLog.d.ts +12 -2
  105. package/dist/hooks/useResolvedNavigationFrom.d.ts +3 -3
  106. package/dist/hooks/useStorageSource.d.ts +2 -2
  107. package/dist/hooks/useValidateAuthenticator.d.ts +21 -0
  108. package/dist/index.d.ts +1 -0
  109. package/dist/index.es.js +22131 -14039
  110. package/dist/index.es.js.map +1 -1
  111. package/dist/index.umd.js +24420 -588
  112. package/dist/index.umd.js.map +1 -1
  113. package/dist/internal/useBuildDataSource.d.ts +1 -12
  114. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  115. package/dist/preview/PropertyPreview.d.ts +1 -1
  116. package/dist/preview/PropertyPreviewProps.d.ts +7 -5
  117. package/dist/preview/components/BooleanPreview.d.ts +5 -1
  118. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  119. package/dist/preview/components/ReferencePreview.d.ts +4 -9
  120. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  121. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  122. package/dist/preview/util.d.ts +3 -3
  123. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  124. package/dist/routes/FireCMSRoute.d.ts +1 -0
  125. package/dist/routes/HomePageRoute.d.ts +3 -0
  126. package/dist/types/analytics.d.ts +1 -1
  127. package/dist/types/auth.d.ts +37 -3
  128. package/dist/types/collections.d.ts +86 -24
  129. package/dist/types/datasource.d.ts +53 -37
  130. package/dist/types/dialogs_controller.d.ts +7 -3
  131. package/dist/types/entities.d.ts +8 -2
  132. package/dist/types/entity_actions.d.ts +28 -4
  133. package/dist/types/entity_callbacks.d.ts +16 -16
  134. package/dist/types/entity_overrides.d.ts +6 -0
  135. package/dist/types/export_import.d.ts +4 -4
  136. package/dist/types/fields.d.ts +71 -42
  137. package/dist/types/firecms.d.ts +6 -2
  138. package/dist/types/firecms_context.d.ts +1 -1
  139. package/dist/types/index.d.ts +2 -1
  140. package/dist/types/navigation.d.ts +45 -26
  141. package/dist/types/permissions.d.ts +8 -4
  142. package/dist/types/plugins.d.ts +40 -31
  143. package/dist/types/properties.d.ts +54 -26
  144. package/dist/types/property_config.d.ts +2 -4
  145. package/dist/types/roles.d.ts +31 -0
  146. package/dist/types/side_dialogs_controller.d.ts +10 -0
  147. package/dist/types/side_entity_controller.d.ts +6 -1
  148. package/dist/types/storage.d.ts +86 -3
  149. package/dist/types/user.d.ts +6 -0
  150. package/dist/util/builders.d.ts +3 -3
  151. package/dist/util/collections.d.ts +9 -1
  152. package/dist/util/entities.d.ts +3 -3
  153. package/dist/util/entity_actions.d.ts +2 -0
  154. package/dist/util/entity_cache.d.ts +23 -0
  155. package/dist/util/icon_list.d.ts +5 -1
  156. package/dist/util/icon_synonyms.d.ts +1 -98
  157. package/dist/util/icons.d.ts +13 -4
  158. package/dist/util/index.d.ts +2 -0
  159. package/dist/util/navigation_from_path.d.ts +6 -1
  160. package/dist/util/navigation_utils.d.ts +14 -3
  161. package/dist/util/objects.d.ts +2 -1
  162. package/dist/util/permissions.d.ts +4 -4
  163. package/dist/util/plurals.d.ts +0 -2
  164. package/dist/util/property_utils.d.ts +3 -3
  165. package/dist/util/references.d.ts +4 -2
  166. package/dist/util/resolutions.d.ts +31 -17
  167. package/dist/util/storage.d.ts +23 -2
  168. package/dist/util/useStorageUploadController.d.ts +3 -3
  169. package/dist/util/useTraceUpdate.d.ts +1 -0
  170. package/package.json +64 -51
  171. package/src/app/AppBar.tsx +18 -0
  172. package/src/app/Drawer.tsx +24 -0
  173. package/src/app/Scaffold.tsx +243 -0
  174. package/src/app/index.ts +4 -0
  175. package/src/app/useApp.tsx +32 -0
  176. package/src/components/ArrayContainer.tsx +126 -40
  177. package/src/components/CircularProgressCenter.tsx +2 -2
  178. package/src/components/ClearFilterSortButton.tsx +41 -0
  179. package/src/components/{DeleteConfirmationDialog.tsx → ConfirmationDialog.tsx} +3 -3
  180. package/src/components/DeleteEntityDialog.tsx +14 -23
  181. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +56 -39
  182. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +283 -280
  183. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +29 -8
  184. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +58 -50
  185. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  186. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +40 -33
  187. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +22 -20
  188. package/src/components/EntityCollectionTable/index.tsx +1 -1
  189. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +31 -36
  190. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +36 -34
  191. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +20 -8
  192. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +81 -80
  193. package/src/components/EntityCollectionView/EntityCollectionView.tsx +228 -155
  194. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +9 -8
  195. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  196. package/src/components/EntityCollectionView/useSelectionController.tsx +43 -0
  197. package/src/components/EntityCollectionView/utils.ts +19 -0
  198. package/src/components/EntityPreview.tsx +218 -71
  199. package/src/components/EntityView.tsx +84 -0
  200. package/src/components/ErrorView.tsx +4 -4
  201. package/src/components/FieldCaption.tsx +14 -0
  202. package/src/components/HomePage/DefaultHomePage.tsx +39 -28
  203. package/src/components/HomePage/FavouritesView.tsx +6 -11
  204. package/src/components/HomePage/NavigationCard.tsx +69 -0
  205. package/src/components/HomePage/NavigationCardBinding.tsx +111 -0
  206. package/src/components/HomePage/NavigationGroup.tsx +2 -2
  207. package/src/components/HomePage/SmallNavigationCard.tsx +44 -0
  208. package/src/components/HomePage/index.tsx +3 -1
  209. package/src/components/NotFoundPage.tsx +2 -2
  210. package/src/components/PropertyConfigBadge.tsx +9 -3
  211. package/src/components/PropertyIdCopyTooltip.tsx +47 -0
  212. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +22 -17
  213. package/src/components/ReferenceWidget.tsx +26 -16
  214. package/src/components/SearchIconsView.tsx +10 -7
  215. package/src/components/SelectableTable/SelectableTable.tsx +27 -12
  216. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  217. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +25 -8
  218. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +46 -30
  219. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +92 -23
  220. package/src/components/UnsavedChangesDialog.tsx +42 -0
  221. package/src/components/VirtualTable/VirtualTable.tsx +96 -48
  222. package/src/components/VirtualTable/VirtualTableCell.tsx +1 -9
  223. package/src/components/VirtualTable/VirtualTableHeader.tsx +10 -10
  224. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  225. package/src/components/VirtualTable/VirtualTableProps.tsx +34 -15
  226. package/src/components/VirtualTable/VirtualTableRow.tsx +5 -6
  227. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +5 -5
  228. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +3 -2
  229. package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +2 -1
  230. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +16 -28
  231. package/src/components/VirtualTable/types.tsx +2 -3
  232. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +40 -43
  233. package/src/components/common/index.ts +2 -1
  234. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  235. package/src/components/common/types.tsx +4 -6
  236. package/src/components/common/useColumnsIds.tsx +24 -3
  237. package/src/components/common/useDataSourceTableController.tsx +420 -0
  238. package/src/components/common/useDebouncedCallback.tsx +20 -0
  239. package/src/components/common/useScrollRestoration.tsx +68 -0
  240. package/src/components/common/useTableSearchHelper.ts +53 -12
  241. package/src/components/index.tsx +8 -5
  242. package/src/contexts/AuthControllerContext.tsx +1 -1
  243. package/src/contexts/BreacrumbsContext.tsx +38 -0
  244. package/src/contexts/DialogsProvider.tsx +5 -4
  245. package/src/contexts/ModeController.tsx +1 -3
  246. package/src/contexts/SnackbarProvider.tsx +2 -0
  247. package/src/core/DefaultAppBar.tsx +207 -0
  248. package/src/core/DefaultDrawer.tsx +185 -0
  249. package/src/core/DrawerNavigationItem.tsx +66 -0
  250. package/src/core/EntityEditView.tsx +455 -0
  251. package/src/core/EntityEditViewFormActions.tsx +199 -0
  252. package/src/core/EntitySidePanel.tsx +87 -18
  253. package/src/core/FireCMS.tsx +67 -44
  254. package/src/core/FireCMSRouter.tsx +17 -0
  255. package/src/core/NavigationRoutes.tsx +36 -38
  256. package/src/core/SideDialogs.tsx +22 -12
  257. package/src/core/field_configs.tsx +11 -12
  258. package/src/core/index.tsx +6 -7
  259. package/src/form/EntityForm.tsx +565 -519
  260. package/src/form/EntityFormActions.tsx +169 -0
  261. package/src/form/PropertyFieldBinding.tsx +85 -44
  262. package/src/form/components/CustomIdField.tsx +6 -2
  263. package/src/form/components/FieldHelperText.tsx +4 -4
  264. package/src/form/components/FormEntry.tsx +22 -0
  265. package/src/form/components/FormLayout.tsx +16 -0
  266. package/src/form/components/LabelWithIcon.tsx +44 -0
  267. package/src/form/components/LabelWithIconAndTooltip.tsx +28 -0
  268. package/src/form/components/StorageItemPreview.tsx +23 -11
  269. package/src/form/components/StorageUploadProgress.tsx +9 -9
  270. package/src/form/components/index.tsx +4 -1
  271. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +32 -21
  272. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +54 -45
  273. package/src/form/field_bindings/BlockFieldBinding.tsx +57 -36
  274. package/src/form/field_bindings/DateTimeFieldBinding.tsx +21 -17
  275. package/src/form/field_bindings/KeyValueFieldBinding.tsx +76 -67
  276. package/src/form/field_bindings/MapFieldBinding.tsx +82 -65
  277. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +153 -0
  278. package/src/form/field_bindings/{MultiSelectBinding.tsx → MultiSelectFieldBinding.tsx} +26 -22
  279. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +12 -17
  280. package/src/form/field_bindings/ReferenceFieldBinding.tsx +47 -34
  281. package/src/form/field_bindings/RepeatFieldBinding.tsx +57 -36
  282. package/src/form/field_bindings/SelectFieldBinding.tsx +24 -17
  283. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +47 -46
  284. package/src/form/field_bindings/SwitchFieldBinding.tsx +31 -27
  285. package/src/form/field_bindings/TextFieldBinding.tsx +35 -30
  286. package/src/form/index.tsx +17 -37
  287. package/src/form/useClearRestoreValue.tsx +2 -2
  288. package/src/form/validation.ts +15 -26
  289. package/src/hooks/data/delete.ts +7 -6
  290. package/src/hooks/data/save.ts +13 -9
  291. package/src/hooks/data/useCollectionFetch.tsx +4 -4
  292. package/src/hooks/data/useDataSource.tsx +15 -2
  293. package/src/hooks/data/useEntityFetch.tsx +11 -7
  294. package/src/hooks/index.tsx +6 -1
  295. package/src/hooks/useAuthController.tsx +1 -1
  296. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  297. package/src/{core → hooks}/useBuildLocalConfigurationPersistence.tsx +8 -10
  298. package/src/{core → hooks}/useBuildModeController.tsx +23 -30
  299. package/src/hooks/useBuildNavigationController.tsx +278 -117
  300. package/src/hooks/useFireCMSContext.tsx +5 -5
  301. package/src/hooks/useLargeLayout.tsx +0 -35
  302. package/src/hooks/useModeController.tsx +1 -2
  303. package/src/hooks/useProjectLog.tsx +41 -9
  304. package/src/hooks/useReferenceDialog.tsx +2 -2
  305. package/src/hooks/useResolvedNavigationFrom.tsx +10 -12
  306. package/src/hooks/useStorageSource.tsx +7 -2
  307. package/src/hooks/useValidateAuthenticator.tsx +115 -0
  308. package/src/index.ts +1 -0
  309. package/src/internal/useBuildDataSource.ts +72 -77
  310. package/src/internal/useBuildSideDialogsController.tsx +4 -2
  311. package/src/internal/useBuildSideEntityController.tsx +177 -70
  312. package/src/internal/useUnsavedChangesDialog.tsx +127 -91
  313. package/src/preview/PropertyPreview.tsx +19 -26
  314. package/src/preview/PropertyPreviewProps.tsx +5 -9
  315. package/src/preview/components/BooleanPreview.tsx +19 -4
  316. package/src/preview/components/EmptyValue.tsx +1 -1
  317. package/src/preview/components/EnumValuesChip.tsx +2 -2
  318. package/src/preview/components/ImagePreview.tsx +25 -36
  319. package/src/preview/components/ReferencePreview.tsx +73 -176
  320. package/src/preview/components/StorageThumbnail.tsx +5 -1
  321. package/src/preview/components/UrlComponentPreview.tsx +60 -28
  322. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +2 -3
  323. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +4 -3
  324. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +2 -3
  325. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +1 -2
  326. package/src/preview/property_previews/ArrayOneOfPreview.tsx +4 -5
  327. package/src/preview/property_previews/ArrayPropertyPreview.tsx +4 -5
  328. package/src/preview/property_previews/MapPropertyPreview.tsx +12 -11
  329. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +12 -12
  330. package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
  331. package/src/preview/util.ts +10 -10
  332. package/src/routes/CustomCMSRoute.tsx +21 -0
  333. package/src/routes/FireCMSRoute.tsx +236 -0
  334. package/src/routes/HomePageRoute.tsx +17 -0
  335. package/src/types/analytics.ts +1 -0
  336. package/src/types/auth.tsx +48 -3
  337. package/src/types/collections.ts +102 -27
  338. package/src/types/customization_controller.tsx +0 -1
  339. package/src/types/datasource.ts +62 -44
  340. package/src/types/dialogs_controller.tsx +7 -3
  341. package/src/types/entities.ts +16 -2
  342. package/src/types/entity_actions.tsx +33 -4
  343. package/src/types/entity_callbacks.ts +18 -18
  344. package/src/types/entity_overrides.tsx +7 -0
  345. package/src/types/export_import.ts +4 -4
  346. package/src/types/fields.tsx +81 -46
  347. package/src/types/firecms.tsx +6 -2
  348. package/src/types/firecms_context.tsx +1 -1
  349. package/src/types/index.ts +2 -1
  350. package/src/types/navigation.ts +52 -31
  351. package/src/types/permissions.ts +10 -5
  352. package/src/types/plugins.tsx +47 -39
  353. package/src/types/properties.ts +63 -27
  354. package/src/types/property_config.tsx +2 -4
  355. package/src/types/roles.ts +41 -0
  356. package/src/types/side_dialogs_controller.tsx +15 -0
  357. package/src/types/side_entity_controller.tsx +7 -1
  358. package/src/types/storage.ts +94 -3
  359. package/src/types/user.ts +9 -0
  360. package/src/util/builders.ts +10 -8
  361. package/src/util/collections.ts +22 -0
  362. package/src/util/entities.ts +9 -6
  363. package/src/util/entity_actions.ts +28 -0
  364. package/src/util/entity_cache.ts +204 -0
  365. package/src/util/enums.ts +1 -1
  366. package/src/util/icon_list.ts +16 -10
  367. package/src/util/icon_synonyms.ts +3 -100
  368. package/src/util/icons.tsx +26 -8
  369. package/src/util/index.ts +2 -0
  370. package/src/util/join_collections.ts +3 -0
  371. package/src/util/make_properties_editable.ts +13 -5
  372. package/src/util/navigation_from_path.ts +13 -6
  373. package/src/util/navigation_utils.ts +59 -7
  374. package/src/util/objects.ts +45 -28
  375. package/src/util/parent_references_from_path.ts +3 -3
  376. package/src/util/permissions.ts +20 -16
  377. package/src/util/plurals.ts +0 -2
  378. package/src/util/property_utils.tsx +11 -4
  379. package/src/util/references.ts +45 -5
  380. package/src/util/resolutions.ts +79 -46
  381. package/src/util/storage.ts +79 -21
  382. package/src/util/strings.ts +2 -2
  383. package/src/util/useStorageUploadController.tsx +71 -27
  384. package/src/util/useTraceUpdate.tsx +2 -1
  385. package/dist/components/PropertyIdCopyTooltipContent.d.ts +0 -3
  386. package/dist/components/VirtualTable/common.d.ts +0 -2
  387. package/dist/core/Drawer.d.ts +0 -23
  388. package/dist/core/Scaffold.d.ts +0 -55
  389. package/dist/form/components/FormikArrayContainer.d.ts +0 -18
  390. package/dist/form/field_bindings/MarkdownFieldBinding.d.ts +0 -9
  391. package/dist/internal/EntityView.d.ts +0 -22
  392. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  393. package/dist/internal/useLocaleConfig.d.ts +0 -1
  394. package/dist/types/appcheck.d.ts +0 -26
  395. package/src/components/FireCMSAppBar.tsx +0 -157
  396. package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
  397. package/src/components/LabelWithIcon.tsx +0 -33
  398. package/src/components/PropertyIdCopyTooltipContent.tsx +0 -28
  399. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -225
  400. package/src/core/Drawer.tsx +0 -164
  401. package/src/core/Scaffold.tsx +0 -281
  402. package/src/form/components/FormikArrayContainer.tsx +0 -44
  403. package/src/form/field_bindings/MarkdownFieldBinding.tsx +0 -696
  404. package/src/internal/EntityView.tsx +0 -578
  405. package/src/internal/useBuildCustomizationController.tsx +0 -5
  406. package/src/internal/useLocaleConfig.tsx +0 -18
  407. package/src/types/appcheck.ts +0 -29
  408. /package/dist/{core → hooks}/useBuildLocalConfigurationPersistence.d.ts +0 -0
  409. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -1,5 +1,4 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
- import { useLocation } from "react-router-dom";
3
2
  import equal from "react-fast-compare"
4
3
 
5
4
  import {
@@ -10,30 +9,38 @@ import {
10
9
  EntityCollection,
11
10
  EntityCollectionsBuilder,
12
11
  EntityReference,
12
+ NavigationBlocker,
13
13
  NavigationController,
14
+ PermissionsBuilder,
14
15
  TopNavigationEntry,
15
16
  TopNavigationResult,
16
17
  User,
17
18
  UserConfigurationPersistence
18
19
  } from "../types";
19
20
  import {
21
+ applyPermissionsFunctionIfEmpty,
20
22
  getCollectionByPathOrId,
21
23
  mergeDeep,
24
+ removeFunctions,
22
25
  removeInitialAndTrailingSlashes,
23
26
  resolveCollectionPathIds,
24
27
  resolvePermissions
25
28
  } from "../util";
26
29
  import { getParentReferencesFromPath } from "../util/parent_references_from_path";
30
+ import { useBlocker, useNavigate } from "react-router-dom";
27
31
 
28
32
  const DEFAULT_BASE_PATH = "/";
29
33
  const DEFAULT_COLLECTION_PATH = "/c";
30
34
 
31
- type BuildNavigationContextProps<EC extends EntityCollection, UserType extends User> = {
35
+ export type BuildNavigationContextProps<EC extends EntityCollection, USER extends User> = {
32
36
  basePath?: string,
33
37
  baseCollectionPath?: string,
34
- authController: AuthController<UserType>;
38
+ authController: AuthController<USER>;
35
39
  collections?: EC[] | EntityCollectionsBuilder<EC>;
40
+ collectionPermissions?: PermissionsBuilder;
36
41
  views?: CMSView[] | CMSViewsBuilder;
42
+ adminViews?: CMSView[] | CMSViewsBuilder;
43
+ viewsOrder?: string[];
37
44
  userConfigPersistence?: UserConfigurationPersistence;
38
45
  dataSourceDelegate: DataSourceDelegate;
39
46
  /**
@@ -44,24 +51,35 @@ type BuildNavigationContextProps<EC extends EntityCollection, UserType extends U
44
51
  * @param collections
45
52
  */
46
53
  injectCollections?: (collections: EntityCollection[]) => EntityCollection[];
54
+
55
+ /**
56
+ * If true, the navigation logic will not be updated until this flag is false
57
+ */
58
+ disabled?: boolean;
47
59
  };
48
60
 
49
- export function useBuildNavigationController<EC extends EntityCollection, UserType extends User>({
50
- basePath = DEFAULT_BASE_PATH,
51
- baseCollectionPath = DEFAULT_COLLECTION_PATH,
52
- authController,
53
- collections: collectionsProp,
54
- views: baseViews,
55
- userConfigPersistence,
56
- dataSourceDelegate,
57
- injectCollections
58
- }: BuildNavigationContextProps<EC, UserType>): NavigationController {
59
-
60
- const location = useLocation();
61
-
62
- const collectionsRef = useRef<EntityCollection[] | null>();
63
- const [collections, setCollections] = useState<EntityCollection[] | undefined>();
64
- const [views, setViews] = useState<CMSView[] | undefined>();
61
+ export function useBuildNavigationController<EC extends EntityCollection, USER extends User>(props: BuildNavigationContextProps<EC, USER>): NavigationController {
62
+ const {
63
+ basePath = DEFAULT_BASE_PATH,
64
+ baseCollectionPath = DEFAULT_COLLECTION_PATH,
65
+ authController,
66
+ collections: collectionsProp,
67
+ collectionPermissions,
68
+ views: viewsProp,
69
+ adminViews: adminViewsProp,
70
+ viewsOrder,
71
+ userConfigPersistence,
72
+ dataSourceDelegate,
73
+ injectCollections,
74
+ disabled
75
+ } = props;
76
+
77
+ const navigate = useNavigate();
78
+
79
+ const collectionsRef = useRef<EntityCollection[] | undefined>();
80
+ const viewsRef = useRef<CMSView[] | undefined>();
81
+ const adminViewsRef = useRef<CMSView[] | undefined>();
82
+
65
83
  const [initialised, setInitialised] = useState<boolean>(false);
66
84
 
67
85
  const [topLevelNavigation, setTopLevelNavigation] = useState<TopNavigationResult | undefined>(undefined);
@@ -81,18 +99,18 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
81
99
  const buildUrlCollectionPath = useCallback((path: string): string => `${removeInitialAndTrailingSlashes(baseCollectionPath)}/${encodePath(path)}`,
82
100
  [baseCollectionPath]);
83
101
 
84
- const computeTopNavigation = useCallback((collections: EntityCollection[], views: CMSView[]): TopNavigationResult => {
85
- const navigationEntries: TopNavigationEntry[] = [
102
+ const computeTopNavigation = useCallback((collections: EntityCollection[], views: CMSView[], adminViews: CMSView[], viewsOrder?: string[]): TopNavigationResult => {
103
+ let navigationEntries: TopNavigationEntry[] = [
86
104
  ...(collections ?? []).map(collection => (!collection.hideFromNavigation
87
- ? {
105
+ ? ({
88
106
  url: buildUrlCollectionPath(collection.id ?? collection.path),
89
107
  type: "collection",
90
108
  name: collection.name.trim(),
91
109
  path: collection.id ?? collection.path,
92
110
  collection,
93
111
  description: collection.description?.trim(),
94
- group: collection.group?.trim()
95
- }
112
+ group: getGroup(collection)
113
+ } satisfies TopNavigationEntry)
96
114
  : undefined))
97
115
  .filter(Boolean) as TopNavigationEntry[],
98
116
  ...(views ?? []).map(view =>
@@ -101,18 +119,74 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
101
119
  url: buildCMSUrlPath(Array.isArray(view.path) ? view.path[0] : view.path),
102
120
  name: view.name.trim(),
103
121
  type: "view",
122
+ path: view.path,
104
123
  view,
105
124
  description: view.description?.trim(),
106
- group: view.group?.trim()
107
- })
125
+ group: getGroup(view)
126
+ } satisfies TopNavigationEntry)
127
+ : undefined)
128
+ .filter(Boolean) as TopNavigationEntry[],
129
+ ...(adminViews ?? []).map(view =>
130
+ !view.hideFromNavigation
131
+ ? ({
132
+ url: buildCMSUrlPath(Array.isArray(view.path) ? view.path[0] : view.path),
133
+ name: view.name.trim(),
134
+ type: "admin",
135
+ path: view.path,
136
+ view,
137
+ description: view.description?.trim(),
138
+ group: "Admin"
139
+ } satisfies TopNavigationEntry)
108
140
  : undefined)
109
141
  .filter(Boolean) as TopNavigationEntry[]
110
142
  ];
111
143
 
144
+ // Sort by group, entries with group "Admin" will go last, and second to last will be the group "Views"
145
+ navigationEntries = navigationEntries.sort((a, b) => {
146
+ if (a.group !== "Views" && a.group !== "Admin" && (b.group === "Views" || b.group === "Admin")) {
147
+ return -1;
148
+ }
149
+ if (b.group !== "Views" && b.group !== "Admin" && (a.group === "Views" || a.group === "Admin")) {
150
+ return 1;
151
+ }
152
+ if (a.group === "Admin" && b.group !== "Admin") {
153
+ return 1;
154
+ }
155
+ if (a.group !== "Admin" && b.group === "Admin") {
156
+ return -1;
157
+ }
158
+ if (a.group === "Views" && b.group !== "Views") {
159
+ return -1;
160
+ }
161
+ if (a.group !== "Views" && b.group === "Views") {
162
+ return 1;
163
+ }
164
+ return 0;
165
+
166
+ });
167
+
168
+ if (viewsOrder) {
169
+ navigationEntries = navigationEntries.sort((a, b) => {
170
+ const aIndex = viewsOrder.indexOf(a.path);
171
+ const bIndex = viewsOrder.indexOf(b.path);
172
+ if (aIndex === -1 && bIndex === -1) {
173
+ return 0;
174
+ }
175
+ if (aIndex === -1) {
176
+ return 1;
177
+ }
178
+ if (bIndex === -1) {
179
+ return -1;
180
+ }
181
+ return aIndex - bIndex;
182
+ });
183
+ }
184
+
112
185
  const groups: string[] = Object.values(navigationEntries)
113
186
  .map(e => e.group)
114
187
  .filter(Boolean)
115
188
  .filter((value, index, array) => array.indexOf(value) === index) as string[];
189
+
116
190
  return {
117
191
  navigationEntries,
118
192
  groups
@@ -121,48 +195,81 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
121
195
 
122
196
  const refreshNavigation = useCallback(async () => {
123
197
 
124
- if (authController.initialLoading)
198
+ if (disabled || authController.initialLoading)
125
199
  return;
126
200
 
201
+ console.debug("Refreshing navigation");
202
+
127
203
  try {
128
- const [resolvedCollections = [], resolvedViews = []] = await Promise.all([
129
- resolveCollections(collectionsProp, authController, dataSourceDelegate, injectCollections),
130
- resolveCMSViews(baseViews, authController, dataSourceDelegate)
204
+
205
+ const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([
206
+ resolveCollections(collectionsProp, collectionPermissions, authController, dataSourceDelegate, injectCollections),
207
+ resolveCMSViews(viewsProp, authController, dataSourceDelegate),
208
+ resolveCMSViews(adminViewsProp, authController, dataSourceDelegate)
131
209
  ]
132
210
  );
133
- if (!equal(collectionsRef.current, resolvedCollections) || !equal(views, resolvedViews) || !equal(topLevelNavigation, computeTopNavigation(resolvedCollections, resolvedViews))) {
211
+
212
+ let shouldUpdateTopLevelNav = false;
213
+ if (!areCollectionListsEqual(collectionsRef.current ?? [], resolvedCollections)) {
214
+ collectionsRef.current = resolvedCollections;
215
+ console.log("Collections have changed", resolvedCollections);
216
+ shouldUpdateTopLevelNav = true;
217
+ }
218
+ if (collectionsRef.current === undefined) {
134
219
  collectionsRef.current = resolvedCollections;
135
- setCollections(resolvedCollections);
136
- setViews(resolvedViews);
137
- setTopLevelNavigation(computeTopNavigation(resolvedCollections ?? [], resolvedViews));
220
+ shouldUpdateTopLevelNav = true;
221
+ }
222
+ if (!equal(viewsRef.current, resolvedViews)) {
223
+ viewsRef.current = resolvedViews;
224
+ shouldUpdateTopLevelNav = true;
225
+ }
226
+ if (!equal(adminViewsRef.current, resolvedAdminViews)) {
227
+ adminViewsRef.current = resolvedAdminViews;
228
+ shouldUpdateTopLevelNav = true;
229
+ }
230
+
231
+ const computedTopLevelNav = computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder);
232
+ if (shouldUpdateTopLevelNav && !equal(topLevelNavigation, computedTopLevelNav)) {
233
+ setTopLevelNavigation(computedTopLevelNav);
138
234
  }
139
235
  } catch (e) {
140
236
  console.error(e);
141
237
  setNavigationLoadingError(e as any);
142
238
  }
143
239
 
144
- setNavigationLoading(false);
145
- setInitialised(true);
146
- }, [collectionsProp, authController.user, authController.initialLoading, baseViews, computeTopNavigation, injectCollections]);
240
+ if (navigationLoading)
241
+ setNavigationLoading(false);
242
+ if (!initialised)
243
+ setInitialised(true);
244
+
245
+ }, [
246
+ collectionsProp,
247
+ collectionPermissions,
248
+ authController.user,
249
+ authController.initialLoading,
250
+ disabled,
251
+ viewsProp,
252
+ adminViewsProp,
253
+ computeTopNavigation,
254
+ injectCollections
255
+ ]);
147
256
 
148
257
  useEffect(() => {
149
258
  refreshNavigation();
150
259
  }, [refreshNavigation]);
151
260
 
152
- const getCollection = useCallback(<EC extends EntityCollection>(
261
+ const getCollection = useCallback((
153
262
  idOrPath: string,
154
- entityId?: string,
155
263
  includeUserOverride = false
156
264
  ): EC | undefined => {
157
-
265
+ const collections = collectionsRef.current;
158
266
  if (!collections)
159
267
  return undefined;
160
268
 
161
269
  const baseCollection = getCollectionByPathOrId(removeInitialAndTrailingSlashes(idOrPath), collections);
162
270
 
163
271
  const userOverride = includeUserOverride ? userConfigPersistence?.getCollectionConfig(idOrPath) : undefined;
164
-
165
- const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride) : undefined;
272
+ const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride ?? {}) : undefined;
166
273
 
167
274
  let result: Partial<EntityCollection> | undefined = overriddenCollection;
168
275
 
@@ -182,16 +289,14 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
182
289
 
183
290
  return { ...overriddenCollection, ...result } as EC;
184
291
 
185
- }, [
186
- basePath,
187
- baseCollectionPath,
188
- collections,
189
- ]);
292
+ }, [userConfigPersistence]);
190
293
 
191
294
  const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
192
- let currentCollections = collections;
193
- if (!currentCollections)
194
- throw Error("Collections have not been initialised yet");
295
+
296
+ const collections = collectionsRef.current;
297
+ if (collections === undefined)
298
+ throw Error("getCollectionFromPaths: Collections have not been initialised yet");
299
+ let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
195
300
 
196
301
  for (let i = 0; i < pathSegments.length; i++) {
197
302
  const pathSegment = pathSegments[i];
@@ -205,12 +310,14 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
205
310
 
206
311
  return undefined;
207
312
 
208
- }, [collections]);
313
+ }, []);
209
314
 
210
315
  const getCollectionFromIds = useCallback(<EC extends EntityCollection>(ids: string[]): EC | undefined => {
211
- let currentCollections = collections;
212
- if (!currentCollections)
213
- throw Error("Collections have not been initialised yet");
316
+
317
+ const collections = collectionsRef.current;
318
+ if (collections === undefined)
319
+ throw Error("getCollectionFromIds: Collections have not been initialised yet");
320
+ let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
214
321
 
215
322
  for (let i = 0; i < ids.length; i++) {
216
323
  const id = ids[i];
@@ -224,7 +331,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
224
331
 
225
332
  return undefined;
226
333
 
227
- }, [collections]);
334
+ }, []);
228
335
 
229
336
  const isUrlCollectionPath = useCallback(
230
337
  (path: string): boolean => removeInitialAndTrailingSlashes(path + "/").startsWith(removeInitialAndTrailingSlashes(fullCollectionPath) + "/"),
@@ -236,35 +343,18 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
236
343
  throw Error("Expected path starting with " + fullCollectionPath);
237
344
  }, [fullCollectionPath]);
238
345
 
239
- const buildUrlEditCollectionPath = useCallback(({
240
- path
241
- }: {
242
- path: string
243
- }): string => {
244
- return `s/edit/${encodePath(path)}`;
245
- },
246
- []);
247
-
248
- const resolveAliasesFrom = useCallback((path: string): string => {
249
- if (!collections)
250
- throw Error("Collections have not been initialised yet");
346
+ const resolveIdsFrom = useCallback((path: string): string => {
347
+ const collections = collectionsRef.current ?? [];
251
348
  return resolveCollectionPathIds(path, collections);
252
- }, [collections]);
253
-
254
- const state = location.state as any;
255
- /**
256
- * The location can be overridden if `base_location` is set in the
257
- * state field of the current location. This can happen if you open
258
- * a side entity, like `products`, from a different one, like `users`
259
- */
260
- const baseLocation = state && state.base_location ? state.base_location : location;
349
+ }, []);
261
350
 
262
351
  const getAllParentReferencesForPath = useCallback((path: string): EntityReference[] => {
352
+ const collections = collectionsRef.current ?? [];
263
353
  return getParentReferencesFromPath({
264
354
  path,
265
355
  collections
266
356
  });
267
- }, [collections]);
357
+ }, []);
268
358
 
269
359
  const getParentCollectionIds = useCallback((path: string): string[] => {
270
360
 
@@ -279,35 +369,28 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
279
369
  }
280
370
 
281
371
  // for each odd path segment, get the collection
282
- const parentCollectionIds = result.map(r => getCollectionFromPaths(r)?.id).filter(Boolean) as string[];
283
-
284
- getCollectionFromPaths(oddPathSegments);
285
-
286
- // const allParentCollectionsForPath = getAllParentReferencesForPath(path);
287
- // console.log("allParentCollectionsForPath", allParentCollectionsForPath);
288
- // const parentCollectionIds = allParentCollectionsForPath.map(r => r.id);
289
- console.log("getParentCollectionIds", path, parentCollectionIds);
290
- return parentCollectionIds;
372
+ return result.map(r => getCollectionFromPaths(r)?.id).filter(Boolean) as string[];
291
373
  }, [getAllParentReferencesForPath])
292
374
 
293
375
  const convertIdsToPaths = useCallback((ids: string[]): string[] => {
294
- let currentCollections = collections;
295
- const paths: string[] = [];
296
- for (let i = 0; i < ids.length; i++) {
297
- const id = ids[i];
298
- const collection: EntityCollection | undefined = currentCollections!.find(c => c.id === id);
299
- if (!collection)
300
- throw Error(`Collection with id ${id} not found`);
301
- paths.push(collection.path);
302
- currentCollections = collection.subcollections;
303
- }
304
- return paths;
376
+ const collections = collectionsRef.current;
377
+ let currentCollections = collections;
378
+ const paths: string[] = [];
379
+ for (let i = 0; i < ids.length; i++) {
380
+ const id = ids[i];
381
+ const collection: EntityCollection | undefined = currentCollections!.find(c => c.id === id);
382
+ if (!collection)
383
+ throw Error(`Collection with id ${id} not found`);
384
+ paths.push(collection.path);
385
+ currentCollections = collection.subcollections;
305
386
  }
306
- , [getCollectionFromIds]);
387
+ return paths;
388
+ }, [getCollectionFromIds]);
307
389
 
308
390
  return {
309
- collections,
310
- views,
391
+ collections: collectionsRef.current,
392
+ views: viewsRef.current,
393
+ adminViews: adminViewsRef.current,
311
394
  loading: !initialised || navigationLoading,
312
395
  navigationLoadingError,
313
396
  homeUrl,
@@ -320,25 +403,16 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
320
403
  isUrlCollectionPath,
321
404
  urlPathToDataPath,
322
405
  buildUrlCollectionPath,
323
- buildUrlEditCollectionPath,
324
- buildCMSUrlPath,
325
- resolveAliasesFrom,
406
+ resolveIdsFrom,
326
407
  topLevelNavigation,
327
- baseLocation,
328
408
  refreshNavigation,
329
409
  getParentReferencesFromPath: getAllParentReferencesForPath,
330
410
  getParentCollectionIds,
331
- convertIdsToPaths
411
+ convertIdsToPaths,
412
+ navigate,
332
413
  };
333
414
  }
334
415
 
335
- export function getSidePanelKey(path: string, entityId?: string) {
336
- if (entityId)
337
- return `${removeInitialAndTrailingSlashes(path)}/${removeInitialAndTrailingSlashes(entityId)}`;
338
- else
339
- return removeInitialAndTrailingSlashes(path);
340
- }
341
-
342
416
  function encodePath(input: string) {
343
417
  return encodeURIComponent(removeInitialAndTrailingSlashes(input))
344
418
  .replaceAll("%2F", "/")
@@ -347,10 +421,11 @@ function encodePath(input: string) {
347
421
 
348
422
  function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[], authController: AuthController<User>): EntityCollection[] {
349
423
  return resolvedCollections
424
+ .filter((c) => Boolean(c.path))
350
425
  .filter((c) => {
351
426
  if (!c.permissions) return true;
352
- const resolvedPermissions = resolvePermissions(c, authController, [c.path], null,)
353
- return resolvedPermissions.read !== false;
427
+ const resolvedPermissions = resolvePermissions(c, authController, c.path, null);
428
+ return resolvedPermissions?.read !== false;
354
429
  })
355
430
  .map((c) => {
356
431
  if (!c.subcollections) return c;
@@ -362,9 +437,10 @@ function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[],
362
437
  }
363
438
 
364
439
  async function resolveCollections(collections: undefined | EntityCollection[] | EntityCollectionsBuilder<any>,
440
+ collectionPermissions: PermissionsBuilder | undefined,
365
441
  authController: AuthController,
366
442
  dataSource: DataSourceDelegate,
367
- injectCollections?: (collections: EntityCollection[]) => EntityCollection[]) {
443
+ injectCollections?: (collections: EntityCollection[]) => EntityCollection[]): Promise<EntityCollection[]> {
368
444
  let resolvedCollections: EntityCollection[] = [];
369
445
  if (typeof collections === "function") {
370
446
  resolvedCollections = await collections({
@@ -376,12 +452,12 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
376
452
  resolvedCollections = collections;
377
453
  }
378
454
 
379
- resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
380
-
381
455
  if (injectCollections) {
382
456
  resolvedCollections = injectCollections(resolvedCollections ?? []);
383
457
  }
384
458
 
459
+ resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
460
+ resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
385
461
  return resolvedCollections;
386
462
  }
387
463
 
@@ -398,3 +474,88 @@ async function resolveCMSViews(baseViews: CMSView[] | CMSViewsBuilder | undefine
398
474
  }
399
475
  return resolvedViews;
400
476
  }
477
+
478
+ function getGroup(collectionOrView: EntityCollection<any, any> | CMSView) {
479
+ const trimmed = collectionOrView.group?.trim();
480
+ if (!trimmed || trimmed === "") {
481
+ return "Views";
482
+ }
483
+ return trimmed ?? "Views";
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(removeFunctions(restA), removeFunctions(restB));
510
+ }
511
+
512
+ function useCustomBlocker(): NavigationBlocker {
513
+ const [blockListeners, setBlockListeners] = useState<Record<string, {
514
+ block: boolean,
515
+ basePath?: string
516
+ }>>({});
517
+
518
+ const shouldBlock = Object.values(blockListeners).some(b => b.block);
519
+
520
+ let blocker: any;
521
+ try {
522
+ blocker = useBlocker(({
523
+ nextLocation
524
+ }) => {
525
+ const allBasePaths = Object.values(blockListeners).map(b => b.basePath).filter(Boolean) as string[];
526
+ if (allBasePaths && allBasePaths.some(path => nextLocation.pathname.startsWith(path)))
527
+ return false;
528
+ return shouldBlock;
529
+ });
530
+ } catch (e) {
531
+ console.warn("Blocker not available, navigation will not be blocked");
532
+ }
533
+
534
+ const updateBlockListener = (path: string, block: boolean, basePath?: string) => {
535
+ setBlockListeners(prev => ({
536
+ ...prev,
537
+ [path]: {
538
+ block,
539
+ basePath
540
+ }
541
+ }));
542
+ return () => setBlockListeners(prev => {
543
+ const {
544
+ [path]: removed,
545
+ ...rest
546
+ } = prev;
547
+ return rest;
548
+ })
549
+ };
550
+
551
+ const isBlocked = (path: string) => {
552
+ return (blockListeners[path]?.block ?? false) && blocker?.state === "blocked";
553
+ }
554
+
555
+ return {
556
+ updateBlockListener,
557
+ isBlocked,
558
+ proceed: blocker?.proceed,
559
+ reset: blocker?.reset
560
+ }
561
+ }
@@ -21,9 +21,9 @@ import React, { useEffect } from "react";
21
21
  * @see FireCMSContext
22
22
  * @group Hooks and utilities
23
23
  */
24
- export const useFireCMSContext = <UserType extends User = User, AuthControllerType extends AuthController<UserType> = AuthController<UserType>>(): FireCMSContext<UserType, AuthControllerType> => {
24
+ export const useFireCMSContext = <USER extends User = User, AuthControllerType extends AuthController<USER> = AuthController<USER>>(): FireCMSContext<USER, AuthControllerType> => {
25
25
 
26
- const authController = useAuthController<UserType, AuthControllerType>();
26
+ const authController = useAuthController<USER, AuthControllerType>();
27
27
  const sideDialogsController = useSideDialogsController();
28
28
  const sideEntityController = useSideEntityController();
29
29
  const navigation = useNavigationController();
@@ -35,7 +35,7 @@ export const useFireCMSContext = <UserType extends User = User, AuthControllerTy
35
35
  const customizationController = useCustomizationController();
36
36
  const analyticsController = useAnalyticsController();
37
37
 
38
- const fireCMSContextRef = React.useRef<FireCMSContext<UserType, AuthControllerType>>({
38
+ const fireCMSContextRef = React.useRef<FireCMSContext<USER, AuthControllerType>>({
39
39
  authController,
40
40
  sideDialogsController,
41
41
  sideEntityController,
@@ -68,9 +68,9 @@ export const useFireCMSContext = <UserType extends User = User, AuthControllerTy
68
68
  return fireCMSContextRef.current;
69
69
  }
70
70
 
71
- // export const useFireCMSContext = <UserType extends User = User, AuthControllerType extends AuthController<UserType> = AuthController<UserType>>(): FireCMSContext<UserType, AuthControllerType> => {
71
+ // export const useFireCMSContext = <USER extends User = User, AuthControllerType extends AuthController<USER> = AuthController<USER>>(): FireCMSContext<USER, AuthControllerType> => {
72
72
  //
73
- // const authController = useAuthController<UserType, AuthControllerType>();
73
+ // const authController = useAuthController<USER, AuthControllerType>();
74
74
  // const sideDialogsController = useSideDialogsController();
75
75
  // const sideEntityController = useSideEntityController();
76
76
  // const navigation = useNavigationController();
@@ -63,38 +63,3 @@ function checkLargeLayout(breakpoint: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" |
63
63
  return false;
64
64
  return window.matchMedia(`(min-width: ${breakpoints[breakpoint] + 1}px)`).matches;
65
65
  }
66
-
67
- // import { useEffect, useState } from "react";
68
- //
69
- // const breakpoints = {
70
- // xs: 0,
71
- // sm: 640,
72
- // md: 768,
73
- // lg: 1024,
74
- // xl: 1280,
75
- // "2xl": 1536,
76
- // "3xl": 1920
77
- // }
78
- // export const useLargeLayout = (breakpoint: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" = "lg"): boolean => {
79
- // const [isLargeLayout, setIsLargeLayout] = useState<boolean>(false);
80
- //
81
- // useEffect(() => {
82
- // const handleResize = () => {
83
- // const matched = window.matchMedia(`(min-width: ${breakpoints[breakpoint] + 1}px)`).matches;
84
- // setIsLargeLayout(matched);
85
- // };
86
- //
87
- // // Set initial state
88
- // handleResize();
89
- //
90
- // // Set up event listener for resize events
91
- // window.addEventListener("resize", handleResize);
92
- //
93
- // // Clean up event listener when component unmounts
94
- // return () => {
95
- // window.removeEventListener("resize", handleResize);
96
- // };
97
- // }, []);
98
- //
99
- // return isLargeLayout;
100
- // };