@firecms/core 3.0.0-canary.26 → 3.0.0-canary.260

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 (405) hide show
  1. package/README.md +3 -3
  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 +34 -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 +31 -12
  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 +14 -13
  12. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  13. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +22 -6
  14. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
  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 +20 -2
  22. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  23. package/dist/components/EntityCollectionView/utils.d.ts +3 -0
  24. package/dist/components/EntityJsonPreview.d.ts +3 -0
  25. package/dist/components/EntityPreview.d.ts +8 -5
  26. package/dist/components/ErrorView.d.ts +1 -1
  27. package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
  28. package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
  29. package/dist/components/HomePage/NavigationCard.d.ts +3 -1
  30. package/dist/components/HomePage/NavigationCardBinding.d.ts +4 -3
  31. package/dist/components/HomePage/NavigationGroup.d.ts +7 -1
  32. package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
  33. package/dist/components/PropertyConfigBadge.d.ts +2 -1
  34. package/dist/components/PropertyIdCopyTooltip.d.ts +8 -0
  35. package/dist/components/ReferenceWidget.d.ts +3 -1
  36. package/dist/components/SelectableTable/SelectableTable.d.ts +14 -4
  37. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  38. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  39. package/dist/components/VirtualTable/VirtualTableProps.d.ts +24 -12
  40. package/dist/components/VirtualTable/types.d.ts +3 -3
  41. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -3
  42. package/dist/components/common/index.d.ts +2 -1
  43. package/dist/components/common/table_height.d.ts +5 -0
  44. package/dist/components/common/types.d.ts +4 -6
  45. package/dist/components/common/useColumnsIds.d.ts +3 -1
  46. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +13 -2
  47. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  48. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  49. package/dist/components/index.d.ts +5 -2
  50. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  51. package/dist/core/DefaultAppBar.d.ts +29 -0
  52. package/dist/core/DefaultDrawer.d.ts +19 -0
  53. package/dist/core/DrawerNavigationItem.d.ts +10 -0
  54. package/dist/core/EntityEditView.d.ts +43 -11
  55. package/dist/core/EntityEditViewFormActions.d.ts +2 -0
  56. package/dist/core/FireCMS.d.ts +3 -3
  57. package/dist/core/FireCMSRouter.d.ts +4 -0
  58. package/dist/core/NavigationRoutes.d.ts +2 -3
  59. package/dist/core/SideDialogs.d.ts +4 -2
  60. package/dist/core/field_configs.d.ts +1 -1
  61. package/dist/core/index.d.ts +4 -4
  62. package/dist/form/EntityForm.d.ts +37 -64
  63. package/dist/form/EntityFormActions.d.ts +21 -0
  64. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  65. package/dist/form/components/ErrorFocus.d.ts +1 -1
  66. package/dist/form/components/FieldHelperText.d.ts +3 -3
  67. package/dist/form/components/FormEntry.d.ts +6 -0
  68. package/dist/form/components/FormLayout.d.ts +5 -0
  69. package/dist/form/components/LabelWithIcon.d.ts +1 -1
  70. package/dist/form/components/LabelWithIconAndTooltip.d.ts +15 -0
  71. package/dist/form/components/StorageItemPreview.d.ts +4 -4
  72. package/dist/form/components/index.d.ts +3 -1
  73. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  74. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  75. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  76. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  77. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  78. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +11 -0
  79. package/dist/form/field_bindings/{MultiSelectBinding.d.ts → MultiSelectFieldBinding.d.ts} +1 -1
  80. package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
  81. package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
  82. package/dist/form/field_bindings/ReferenceFieldBinding.d.ts +2 -2
  83. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  84. package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
  85. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +5 -13
  86. package/dist/form/field_bindings/SwitchFieldBinding.d.ts +1 -2
  87. package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
  88. package/dist/form/index.d.ts +17 -18
  89. package/dist/form/useClearRestoreValue.d.ts +2 -2
  90. package/dist/hooks/data/delete.d.ts +4 -4
  91. package/dist/hooks/data/save.d.ts +4 -5
  92. package/dist/hooks/data/useCollectionFetch.d.ts +1 -1
  93. package/dist/hooks/data/useEntityFetch.d.ts +4 -3
  94. package/dist/hooks/index.d.ts +1 -0
  95. package/dist/hooks/useAuthController.d.ts +1 -1
  96. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  97. package/dist/hooks/useBuildNavigationController.d.ts +57 -13
  98. package/dist/hooks/useFireCMSContext.d.ts +1 -1
  99. package/dist/hooks/useModeController.d.ts +1 -2
  100. package/dist/hooks/useProjectLog.d.ts +8 -2
  101. package/dist/hooks/useResolvedNavigationFrom.d.ts +3 -3
  102. package/dist/hooks/useValidateAuthenticator.d.ts +4 -8
  103. package/dist/index.d.ts +1 -0
  104. package/dist/index.es.js +23088 -13940
  105. package/dist/index.es.js.map +1 -1
  106. package/dist/index.umd.js +25823 -588
  107. package/dist/index.umd.js.map +1 -1
  108. package/dist/internal/useBuildDataSource.d.ts +3 -17
  109. package/dist/internal/useBuildSideEntityController.d.ts +3 -3
  110. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  111. package/dist/preview/PropertyPreviewProps.d.ts +6 -1
  112. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  113. package/dist/preview/components/ReferencePreview.d.ts +3 -2
  114. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  115. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  116. package/dist/preview/util.d.ts +3 -3
  117. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  118. package/dist/routes/FireCMSRoute.d.ts +1 -0
  119. package/dist/routes/HomePageRoute.d.ts +3 -0
  120. package/dist/types/analytics.d.ts +1 -1
  121. package/dist/types/auth.d.ts +8 -10
  122. package/dist/types/collections.d.ts +110 -25
  123. package/dist/types/customization_controller.d.ts +8 -0
  124. package/dist/types/datasource.d.ts +52 -36
  125. package/dist/types/dialogs_controller.d.ts +7 -3
  126. package/dist/types/entities.d.ts +7 -2
  127. package/dist/types/entity_actions.d.ts +72 -8
  128. package/dist/types/entity_callbacks.d.ts +16 -16
  129. package/dist/types/entity_overrides.d.ts +2 -2
  130. package/dist/types/export_import.d.ts +4 -4
  131. package/dist/types/fields.d.ts +74 -42
  132. package/dist/types/firecms.d.ts +16 -3
  133. package/dist/types/firecms_context.d.ts +1 -1
  134. package/dist/types/index.d.ts +0 -1
  135. package/dist/types/navigation.d.ts +62 -19
  136. package/dist/types/permissions.d.ts +4 -4
  137. package/dist/types/plugins.d.ts +56 -13
  138. package/dist/types/properties.d.ts +80 -24
  139. package/dist/types/property_config.d.ts +1 -3
  140. package/dist/types/roles.d.ts +3 -0
  141. package/dist/types/side_dialogs_controller.d.ts +10 -0
  142. package/dist/types/side_entity_controller.d.ts +14 -1
  143. package/dist/types/storage.d.ts +75 -0
  144. package/dist/types/user.d.ts +1 -0
  145. package/dist/util/builders.d.ts +3 -3
  146. package/dist/util/callbacks.d.ts +2 -0
  147. package/dist/util/createFormexStub.d.ts +2 -0
  148. package/dist/util/entities.d.ts +3 -3
  149. package/dist/util/entity_actions.d.ts +2 -0
  150. package/dist/util/entity_cache.d.ts +23 -0
  151. package/dist/util/icon_list.d.ts +5 -1
  152. package/dist/util/icon_synonyms.d.ts +1 -98
  153. package/dist/util/icons.d.ts +7 -4
  154. package/dist/util/index.d.ts +3 -0
  155. package/dist/util/navigation_from_path.d.ts +10 -1
  156. package/dist/util/navigation_utils.d.ts +15 -3
  157. package/dist/util/objects.d.ts +2 -1
  158. package/dist/util/permissions.d.ts +4 -4
  159. package/dist/util/plurals.d.ts +0 -2
  160. package/dist/util/property_utils.d.ts +4 -4
  161. package/dist/util/references.d.ts +2 -2
  162. package/dist/util/resolutions.d.ts +42 -17
  163. package/dist/util/storage.d.ts +23 -2
  164. package/dist/util/useStorageUploadController.d.ts +3 -3
  165. package/package.json +64 -48
  166. package/src/app/AppBar.tsx +18 -0
  167. package/src/app/Drawer.tsx +24 -0
  168. package/src/app/Scaffold.tsx +253 -0
  169. package/src/app/index.ts +4 -0
  170. package/src/app/useApp.tsx +32 -0
  171. package/src/components/ArrayContainer.tsx +447 -229
  172. package/src/components/CircularProgressCenter.tsx +2 -2
  173. package/src/components/ClearFilterSortButton.tsx +41 -0
  174. package/src/components/{DeleteConfirmationDialog.tsx → ConfirmationDialog.tsx} +12 -11
  175. package/src/components/DeleteEntityDialog.tsx +13 -20
  176. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +65 -40
  177. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +38 -31
  178. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +30 -9
  179. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +72 -42
  180. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  181. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +30 -16
  182. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +19 -17
  183. package/src/components/EntityCollectionTable/index.tsx +1 -1
  184. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +34 -39
  185. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +49 -36
  186. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +20 -8
  187. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +135 -105
  188. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
  189. package/src/components/EntityCollectionView/EntityCollectionView.tsx +235 -118
  190. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +7 -4
  191. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  192. package/src/components/EntityCollectionView/useSelectionController.tsx +20 -7
  193. package/src/components/EntityCollectionView/utils.ts +19 -0
  194. package/src/components/EntityJsonPreview.tsx +66 -0
  195. package/src/components/EntityPreview.tsx +80 -59
  196. package/src/components/EntityView.tsx +13 -10
  197. package/src/components/ErrorView.tsx +4 -4
  198. package/src/components/FireCMSLogo.tsx +7 -51
  199. package/src/components/HomePage/DefaultHomePage.tsx +499 -157
  200. package/src/components/HomePage/FavouritesView.tsx +9 -14
  201. package/src/components/HomePage/HomePageDnD.tsx +599 -0
  202. package/src/components/HomePage/NavigationCard.tsx +48 -39
  203. package/src/components/HomePage/NavigationCardBinding.tsx +17 -16
  204. package/src/components/HomePage/NavigationGroup.tsx +63 -29
  205. package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
  206. package/src/components/HomePage/SmallNavigationCard.tsx +5 -6
  207. package/src/components/NotFoundPage.tsx +2 -2
  208. package/src/components/PropertyConfigBadge.tsx +9 -3
  209. package/src/components/PropertyIdCopyTooltip.tsx +47 -0
  210. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +21 -13
  211. package/src/components/ReferenceWidget.tsx +21 -11
  212. package/src/components/SearchIconsView.tsx +10 -7
  213. package/src/components/SelectableTable/SelectableTable.tsx +157 -145
  214. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  215. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +25 -8
  216. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +36 -12
  217. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +92 -23
  218. package/src/components/UnsavedChangesDialog.tsx +46 -0
  219. package/src/components/VirtualTable/VirtualTable.tsx +105 -51
  220. package/src/components/VirtualTable/VirtualTableCell.tsx +1 -9
  221. package/src/components/VirtualTable/VirtualTableHeader.tsx +10 -10
  222. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  223. package/src/components/VirtualTable/VirtualTableProps.tsx +28 -14
  224. package/src/components/VirtualTable/VirtualTableRow.tsx +5 -6
  225. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +5 -5
  226. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +2 -2
  227. package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +2 -1
  228. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +16 -28
  229. package/src/components/VirtualTable/types.tsx +2 -3
  230. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +64 -44
  231. package/src/components/common/index.ts +2 -1
  232. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  233. package/src/components/common/types.tsx +4 -6
  234. package/src/components/common/useColumnsIds.tsx +24 -3
  235. package/src/components/common/useDataSourceTableController.tsx +420 -0
  236. package/src/components/common/useDebouncedCallback.tsx +20 -0
  237. package/src/components/common/useScrollRestoration.tsx +68 -0
  238. package/src/components/common/useTableSearchHelper.ts +53 -12
  239. package/src/components/index.tsx +6 -2
  240. package/src/contexts/BreacrumbsContext.tsx +38 -0
  241. package/src/contexts/DialogsProvider.tsx +5 -4
  242. package/src/contexts/ModeController.tsx +1 -3
  243. package/src/contexts/SnackbarProvider.tsx +2 -0
  244. package/src/core/DefaultAppBar.tsx +219 -0
  245. package/src/core/DefaultDrawer.tsx +185 -0
  246. package/src/core/DrawerNavigationItem.tsx +66 -0
  247. package/src/core/EntityEditView.tsx +435 -470
  248. package/src/core/EntityEditViewFormActions.tsx +329 -0
  249. package/src/core/EntitySidePanel.tsx +88 -21
  250. package/src/core/FireCMS.tsx +74 -58
  251. package/src/core/FireCMSRouter.tsx +17 -0
  252. package/src/core/NavigationRoutes.tsx +28 -38
  253. package/src/core/SideDialogs.tsx +22 -12
  254. package/src/core/field_configs.tsx +26 -13
  255. package/src/core/index.tsx +6 -5
  256. package/src/form/EntityForm.tsx +620 -534
  257. package/src/form/EntityFormActions.tsx +211 -0
  258. package/src/form/PropertyFieldBinding.tsx +88 -45
  259. package/src/form/components/CustomIdField.tsx +9 -3
  260. package/src/form/components/FieldHelperText.tsx +4 -4
  261. package/src/form/components/FormEntry.tsx +22 -0
  262. package/src/form/components/FormLayout.tsx +16 -0
  263. package/src/form/components/LabelWithIcon.tsx +30 -19
  264. package/src/form/components/LabelWithIconAndTooltip.tsx +28 -0
  265. package/src/form/components/StorageItemPreview.tsx +23 -13
  266. package/src/form/components/StorageUploadProgress.tsx +5 -6
  267. package/src/form/components/index.tsx +3 -1
  268. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +34 -19
  269. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +50 -36
  270. package/src/form/field_bindings/BlockFieldBinding.tsx +56 -34
  271. package/src/form/field_bindings/DateTimeFieldBinding.tsx +18 -14
  272. package/src/form/field_bindings/KeyValueFieldBinding.tsx +61 -52
  273. package/src/form/field_bindings/MapFieldBinding.tsx +73 -55
  274. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +159 -0
  275. package/src/form/field_bindings/{MultiSelectBinding.tsx → MultiSelectFieldBinding.tsx} +26 -21
  276. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +11 -16
  277. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
  278. package/src/form/field_bindings/ReferenceFieldBinding.tsx +42 -31
  279. package/src/form/field_bindings/RepeatFieldBinding.tsx +62 -35
  280. package/src/form/field_bindings/SelectFieldBinding.tsx +24 -15
  281. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +257 -199
  282. package/src/form/field_bindings/SwitchFieldBinding.tsx +29 -24
  283. package/src/form/field_bindings/TextFieldBinding.tsx +28 -24
  284. package/src/form/index.tsx +17 -37
  285. package/src/form/useClearRestoreValue.tsx +2 -2
  286. package/src/form/validation.ts +13 -23
  287. package/src/hooks/data/delete.ts +6 -5
  288. package/src/hooks/data/save.ts +26 -33
  289. package/src/hooks/data/useCollectionFetch.tsx +3 -3
  290. package/src/hooks/data/useDataSource.tsx +11 -3
  291. package/src/hooks/data/useEntityFetch.tsx +10 -6
  292. package/src/hooks/index.tsx +1 -0
  293. package/src/hooks/useAuthController.tsx +1 -1
  294. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  295. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  296. package/src/hooks/useBuildModeController.tsx +22 -29
  297. package/src/hooks/useBuildNavigationController.tsx +440 -119
  298. package/src/hooks/useFireCMSContext.tsx +3 -33
  299. package/src/hooks/useLargeLayout.tsx +0 -35
  300. package/src/hooks/useModeController.tsx +1 -2
  301. package/src/hooks/useProjectLog.tsx +32 -10
  302. package/src/hooks/useResolvedNavigationFrom.tsx +10 -12
  303. package/src/hooks/useValidateAuthenticator.tsx +17 -37
  304. package/src/index.ts +1 -0
  305. package/src/internal/useBuildDataSource.ts +79 -85
  306. package/src/internal/useBuildSideDialogsController.tsx +4 -2
  307. package/src/internal/useBuildSideEntityController.tsx +204 -77
  308. package/src/internal/useUnsavedChangesDialog.tsx +127 -91
  309. package/src/preview/PropertyPreview.tsx +34 -25
  310. package/src/preview/PropertyPreviewProps.tsx +7 -1
  311. package/src/preview/components/BooleanPreview.tsx +2 -2
  312. package/src/preview/components/EmptyValue.tsx +1 -1
  313. package/src/preview/components/EnumValuesChip.tsx +2 -2
  314. package/src/preview/components/ImagePreview.tsx +26 -37
  315. package/src/preview/components/ReferencePreview.tsx +23 -34
  316. package/src/preview/components/StorageThumbnail.tsx +5 -1
  317. package/src/preview/components/UrlComponentPreview.tsx +60 -28
  318. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +6 -6
  319. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +7 -5
  320. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +5 -4
  321. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -4
  322. package/src/preview/property_previews/ArrayOneOfPreview.tsx +7 -6
  323. package/src/preview/property_previews/ArrayPropertyPreview.tsx +7 -6
  324. package/src/preview/property_previews/MapPropertyPreview.tsx +12 -11
  325. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +13 -13
  326. package/src/preview/property_previews/StringPropertyPreview.tsx +3 -3
  327. package/src/preview/util.ts +10 -10
  328. package/src/routes/CustomCMSRoute.tsx +21 -0
  329. package/src/routes/FireCMSRoute.tsx +246 -0
  330. package/src/routes/HomePageRoute.tsx +17 -0
  331. package/src/types/analytics.ts +3 -0
  332. package/src/types/auth.tsx +9 -13
  333. package/src/types/collections.ts +132 -30
  334. package/src/types/customization_controller.tsx +9 -1
  335. package/src/types/datasource.ts +61 -43
  336. package/src/types/dialogs_controller.tsx +7 -3
  337. package/src/types/entities.ts +12 -2
  338. package/src/types/entity_actions.tsx +86 -10
  339. package/src/types/entity_callbacks.ts +18 -18
  340. package/src/types/entity_overrides.tsx +2 -2
  341. package/src/types/export_import.ts +4 -4
  342. package/src/types/fields.tsx +85 -46
  343. package/src/types/firecms.tsx +18 -4
  344. package/src/types/firecms_context.tsx +1 -1
  345. package/src/types/index.ts +0 -1
  346. package/src/types/navigation.ts +77 -24
  347. package/src/types/permissions.ts +5 -5
  348. package/src/types/plugins.tsx +66 -15
  349. package/src/types/properties.ts +95 -26
  350. package/src/types/property_config.tsx +1 -2
  351. package/src/types/roles.ts +3 -0
  352. package/src/types/side_dialogs_controller.tsx +15 -0
  353. package/src/types/side_entity_controller.tsx +16 -1
  354. package/src/types/storage.ts +83 -1
  355. package/src/types/user.ts +2 -0
  356. package/src/util/builders.ts +10 -8
  357. package/src/util/callbacks.ts +119 -0
  358. package/src/util/createFormexStub.tsx +62 -0
  359. package/src/util/entities.ts +9 -6
  360. package/src/util/entity_actions.ts +28 -0
  361. package/src/util/entity_cache.ts +204 -0
  362. package/src/util/enums.ts +1 -1
  363. package/src/util/icon_list.ts +16 -10
  364. package/src/util/icon_synonyms.ts +3 -100
  365. package/src/util/icons.tsx +36 -11
  366. package/src/util/index.ts +3 -0
  367. package/src/util/join_collections.ts +9 -2
  368. package/src/util/make_properties_editable.ts +13 -5
  369. package/src/util/navigation_from_path.ts +33 -12
  370. package/src/util/navigation_utils.ts +141 -25
  371. package/src/util/objects.ts +90 -33
  372. package/src/util/parent_references_from_path.ts +3 -3
  373. package/src/util/permissions.ts +9 -8
  374. package/src/util/plurals.ts +0 -2
  375. package/src/util/property_utils.tsx +17 -6
  376. package/src/util/references.ts +19 -8
  377. package/src/util/resolutions.ts +122 -48
  378. package/src/util/storage.ts +79 -21
  379. package/src/util/strings.ts +2 -2
  380. package/src/util/useStorageUploadController.tsx +91 -28
  381. package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
  382. package/dist/components/FireCMSAppBar.d.ts +0 -26
  383. package/dist/components/PropertyIdCopyTooltipContent.d.ts +0 -3
  384. package/dist/components/VirtualTable/common.d.ts +0 -2
  385. package/dist/core/Drawer.d.ts +0 -23
  386. package/dist/core/Scaffold.d.ts +0 -55
  387. package/dist/core/SideEntityView.d.ts +0 -7
  388. package/dist/form/components/FormikArrayContainer.d.ts +0 -18
  389. package/dist/form/field_bindings/MarkdownFieldBinding.d.ts +0 -9
  390. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  391. package/dist/internal/useLocaleConfig.d.ts +0 -1
  392. package/dist/types/appcheck.d.ts +0 -26
  393. package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
  394. package/src/components/FireCMSAppBar.tsx +0 -165
  395. package/src/components/PropertyIdCopyTooltipContent.tsx +0 -28
  396. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -225
  397. package/src/core/Drawer.tsx +0 -191
  398. package/src/core/Scaffold.tsx +0 -281
  399. package/src/core/SideEntityView.tsx +0 -38
  400. package/src/form/components/FormikArrayContainer.tsx +0 -44
  401. package/src/form/field_bindings/MarkdownFieldBinding.tsx +0 -695
  402. package/src/internal/useBuildCustomizationController.tsx +0 -5
  403. package/src/internal/useLocaleConfig.tsx +0 -18
  404. package/src/types/appcheck.ts +0 -29
  405. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -1,561 +1,526 @@
1
- import React, { useCallback, useEffect, useRef, useState } from "react";
1
+ import React, { useEffect, useMemo, useState } from "react";
2
2
  import {
3
3
  Entity,
4
4
  EntityCollection,
5
- EntityCustomView,
6
5
  EntityStatus,
7
- EntityValues,
8
6
  FireCMSPlugin,
9
7
  FormContext,
8
+ PluginFormActionProps,
10
9
  User
11
10
  } from "../types";
12
- import { CircularProgressCenter, EntityCollectionView, EntityView, ErrorBoundary, } from "../components";
11
+
12
+ import { CircularProgressCenter, EntityCollectionView, EntityView, ErrorBoundary } from "../components";
13
13
  import {
14
14
  canEditEntity,
15
15
  removeInitialAndTrailingSlashes,
16
+ resolveCollection,
16
17
  resolveDefaultSelectedView,
17
- resolveEntityView,
18
- useDebouncedCallback
18
+ resolvedSelectedEntityView
19
19
  } from "../util";
20
20
 
21
21
  import {
22
- saveEntityWithCallbacks,
23
22
  useAuthController,
24
23
  useCustomizationController,
25
- useDataSource,
26
24
  useEntityFetch,
27
25
  useFireCMSContext,
28
- useSideEntityController,
29
- useSnackbarController
26
+ useLargeLayout
30
27
  } from "../hooks";
31
- import { EntityForm } from "../form";
32
- import { CircularProgress, CloseIcon, cn, defaultBorderMixin, IconButton, Tab, Tabs, Typography } from "@firecms/ui";
33
- import { EntityFormSaveParams } from "../form/EntityForm";
34
- import { FORM_CONTAINER_WIDTH } from "../internal/common";
35
- import { useSideDialogContext } from "./index";
28
+ import { CircularProgress, cls, CodeIcon, defaultBorderMixin, Tab, Tabs, Typography } from "@firecms/ui";
29
+ import { getEntityFromCache } from "../util/entity_cache";
30
+ import { EntityForm, EntityFormProps } from "../form";
31
+ import { EntityEditViewFormActions } from "./EntityEditViewFormActions";
32
+ import { EntityJsonPreview } from "../components/EntityJsonPreview";
33
+ import { createFormexStub } from "../util/createFormexStub";
34
+
35
+ export const MAIN_TAB_VALUE = "__main_##Q$SC^#S6";
36
+ export const JSON_TAB_VALUE = "__json";
37
+
38
+ export type OnUpdateParams = {
39
+ entity: Entity<any>,
40
+ status: EntityStatus,
41
+ path: string,
42
+ entityId?: string;
43
+ selectedTab?: string;
44
+ collection: EntityCollection<any>
45
+ };
46
+
47
+ export type OnTabChangeParams<M extends Record<string, any>> = {
48
+ path: string;
49
+ entityId?: string;
50
+ selectedTab?: string;
51
+ collection: EntityCollection<M>;
36
52
 
37
- const MAIN_TAB_VALUE = "main_##Q$SC^#S6";
53
+ };
38
54
 
39
55
  export interface EntityEditViewProps<M extends Record<string, any>> {
56
+ /**
57
+ * The database path of the entity, e.g. "users" or "products".
58
+ */
40
59
  path: string;
60
+ /**
61
+ * The navigation path to the entity.
62
+ */
63
+ fullIdPath?: string;
41
64
  collection: EntityCollection<M>;
42
65
  entityId?: string;
66
+ databaseId?: string;
43
67
  copy?: boolean;
44
- selectedSubPath?: string;
68
+ selectedTab?: string;
45
69
  parentCollectionIds: string[];
46
- formWidth?: number | string;
47
- onValuesAreModified: (modified: boolean) => void;
48
- onUpdate?: (params: { entity: Entity<any> }) => void;
49
- onClose?: () => void;
70
+ onValuesModified?: (modified: boolean) => void;
71
+ onSaved?: (params: OnUpdateParams) => void;
72
+ onTabChange?: (props: OnTabChangeParams<M>) => void;
73
+ layout?: "side_panel" | "full_screen";
74
+ barActions?: React.ReactNode;
75
+ formProps?: Partial<EntityFormProps<M>>,
50
76
  }
51
77
 
52
78
  /**
53
79
  * This is the default view that is used as the content of a side panel when
54
80
  * an entity is opened.
55
- * You probably don't want to use this view directly since it is bound to the
56
- * side panel. Instead, you might want to use {@link EntityForm} or {@link EntityCollectionView}
57
81
  */
58
- export function EntityEditView<M extends Record<string, any>, UserType extends User>({
59
- path,
60
- entityId,
61
- selectedSubPath,
62
- copy,
63
- collection,
64
- parentCollectionIds,
65
- onValuesAreModified,
66
- formWidth,
67
- onUpdate,
68
- onClose,
69
- }: EntityEditViewProps<M>) {
70
-
71
- if (collection.customId && collection.formAutoSave) {
72
- console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
82
+ export function EntityEditView<M extends Record<string, any>, USER extends User>({
83
+ entityId,
84
+ ...props
85
+ }: EntityEditViewProps<M>) {
86
+
87
+ const {
88
+ entity,
89
+ dataLoading,
90
+ // eslint-disable-next-line no-unused-vars
91
+ dataLoadingError
92
+ } = useEntityFetch<M, USER>({
93
+ path: props.path,
94
+ entityId: entityId,
95
+ collection: props.collection,
96
+ databaseId: props.databaseId,
97
+ useCache: false
98
+ });
99
+
100
+ const cachedValues = entityId
101
+ ? getEntityFromCache(props.path + "/" + entityId)
102
+ : getEntityFromCache(props.path + "#new");
103
+
104
+ const authController = useAuthController();
105
+
106
+ const initialStatus = props.copy ? "copy" : (entityId ? "existing" : "new");
107
+ const [status, setStatus] = useState<EntityStatus>(initialStatus);
108
+
109
+ const canEdit = useMemo(() => {
110
+ if (status === "new" || status === "copy") {
111
+ return true;
112
+ } else {
113
+ return entity ? canEditEntity(props.collection, authController, props.path, entity ?? null) : undefined;
114
+ }
115
+ }, [authController, entity, status]);
116
+
117
+ if ((dataLoading && !cachedValues) || (!entity || canEdit === undefined) && (status === "existing" || status === "copy")) {
118
+ return <CircularProgressCenter/>;
73
119
  }
74
120
 
75
- const [saving, setSaving] = useState(false);
76
- /**
77
- * These are the values that are being saved. They are debounced.
78
- * We use this only when autoSave is enabled.
79
- */
80
- const [valuesToBeSaved, setValuesToBeSaved] = useState<EntityValues<M> | undefined>(undefined);
81
- useDebouncedCallback(valuesToBeSaved, () => {
82
- if (valuesToBeSaved)
83
- saveEntity({
84
- entityId: usedEntity?.id,
85
- collection,
86
- path,
87
- values: valuesToBeSaved,
88
- closeAfterSave: false
89
- });
90
- }, false, 2000);
121
+ if (entityId && !entity && !cachedValues) {
122
+ console.error(`Entity with id ${entityId} not found in collection ${props.path}`);
123
+ }
91
124
 
92
- // const largeLayout = useLargeLayout();
93
- // const largeLayoutTabSelected = useRef(!largeLayout);
125
+ return <EntityEditViewInner<M> {...props}
126
+ entityId={entityId}
127
+ entity={entity}
128
+ cachedDirtyValues={cachedValues as Partial<M>}
129
+ dataLoading={dataLoading}
130
+ status={status}
131
+ setStatus={setStatus}
132
+ canEdit={canEdit}
133
+ />;
134
+ }
94
135
 
95
- const resolvedFormWidth: string = typeof formWidth === "number" ? `${formWidth}px` : formWidth ?? FORM_CONTAINER_WIDTH;
136
+ export function EntityEditViewInner<M extends Record<string, any>>({
137
+ path,
138
+ fullIdPath,
139
+ entityId,
140
+ selectedTab: selectedTabProp,
141
+ collection,
142
+ parentCollectionIds,
143
+ onValuesModified,
144
+ onSaved,
145
+ onTabChange,
146
+ entity,
147
+ cachedDirtyValues,
148
+ dataLoading,
149
+ layout = "side_panel",
150
+ barActions,
151
+ status,
152
+ setStatus,
153
+ formProps,
154
+ canEdit
155
+ }: EntityEditViewProps<M> & {
156
+ entity?: Entity<M>,
157
+ cachedDirtyValues?: Partial<M>, // dirty cached entity in memory
158
+ dataLoading: boolean,
159
+ status: EntityStatus,
160
+ setStatus: (status: EntityStatus) => void,
161
+ canEdit?: boolean,
162
+ }) {
96
163
 
97
- const dataSource = useDataSource(collection);
98
- const sideDialogContext = useSideDialogContext();
99
- const sideEntityController = useSideEntityController();
100
- const snackbarController = useSnackbarController();
101
- const customizationController = useCustomizationController();
102
164
  const context = useFireCMSContext();
103
- const authController = useAuthController<UserType>();
104
165
 
105
- const [formContext, setFormContext] = useState<FormContext<M> | undefined>(undefined);
166
+ const [usedEntity, setUsedEntity] = useState<Entity<M> | undefined>(entity);
167
+
168
+ useEffect(() => {
169
+ if (entity)
170
+ setUsedEntity(entity);
171
+ }, [entity]);
106
172
 
107
- const [status, setStatus] = useState<EntityStatus>(copy ? "copy" : (entityId ? "existing" : "new"));
173
+ const [formContext, setFormContext] = useState<FormContext<M> | undefined>(undefined);
108
174
 
109
- const modifiedValuesRef = useRef<EntityValues<M> | undefined>(undefined);
110
- const modifiedValues = modifiedValuesRef.current;
175
+ const largeLayout = useLargeLayout();
111
176
 
112
- const subcollections = (collection.subcollections ?? []).filter(c => !c.hideFromNavigation);
113
- const subcollectionsCount = subcollections?.length ?? 0;
114
- const customViews = collection.entityViews;
115
- const customViewsCount = customViews?.length ?? 0;
116
- const autoSave = collection.formAutoSave && !collection.customId;
177
+ const customizationController = useCustomizationController();
178
+ const plugins = customizationController.plugins;
179
+ const pluginActionsTop: React.ReactNode[] = [];
117
180
 
118
- const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0;
181
+ if (plugins && collection) {
182
+ const actionProps: PluginFormActionProps = {
183
+ entityId,
184
+ parentCollectionIds,
185
+ path,
186
+ status,
187
+ collection,
188
+ context,
189
+ formContext,
190
+ openEntityMode: layout,
191
+ disabled: false
192
+ };
193
+ pluginActionsTop.push(...plugins.map((plugin) => (
194
+ plugin.form?.ActionsTop
195
+ ? <plugin.form.ActionsTop
196
+ key={`actions_${plugin.key}`} {...actionProps} />
197
+ : null
198
+ )).filter(Boolean));
199
+ }
119
200
 
120
- const defaultSelectedView = selectedSubPath ?? resolveDefaultSelectedView(
201
+ const defaultSelectedView = useMemo(() => resolveDefaultSelectedView(
121
202
  collection ? collection.defaultSelectedView : undefined,
122
203
  {
123
204
  status,
124
205
  entityId
125
206
  }
126
- );
207
+ ), []);
127
208
 
128
- const selectedTabRef = useRef<string>(defaultSelectedView ?? MAIN_TAB_VALUE);
209
+ const [selectedTab, setSelectedTab] = useState<string>(selectedTabProp ?? defaultSelectedView ?? MAIN_TAB_VALUE);
210
+ useEffect(() => {
211
+ if ((selectedTabProp ?? defaultSelectedView ?? MAIN_TAB_VALUE) !== selectedTab) {
212
+ setSelectedTab(selectedTabProp ?? defaultSelectedView ?? MAIN_TAB_VALUE);
213
+ }
214
+ }, [selectedTabProp]);
129
215
 
130
- const mainViewVisible = selectedTabRef.current === MAIN_TAB_VALUE;
216
+ const subcollections = (collection.subcollections ?? []).filter(c => !c.hideFromNavigation);
217
+ const subcollectionsCount = subcollections?.length ?? 0;
218
+ const customViews = collection.entityViews ?? [];
219
+ const customViewsCount = customViews?.length ?? 0;
220
+ const includeJsonView = collection.includeJsonView === undefined ? true : collection.includeJsonView;
221
+ const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0 || includeJsonView;
131
222
 
132
223
  const {
133
- entity,
134
- dataLoading,
135
- // eslint-disable-next-line no-unused-vars
136
- dataLoadingError
137
- } = useEntityFetch<M, UserType>({
138
- path,
139
- entityId,
140
- collection,
141
- useCache: false
142
- });
143
-
144
- const [usedEntity, setUsedEntity] = useState<Entity<M> | undefined>(entity);
145
- const [readOnly, setReadOnly] = useState<boolean | undefined>(undefined);
224
+ resolvedEntityViews,
225
+ selectedEntityView,
226
+ selectedSecondaryForm
227
+ } = resolvedSelectedEntityView(customViews, customizationController, selectedTab, canEdit);
146
228
 
147
- useEffect(() => {
148
- if (entity)
149
- setUsedEntity(entity);
150
- }, [entity]);
229
+ const actionsAtTheBottom = !largeLayout || layout === "side_panel" || selectedEntityView?.includeActions === "bottom";
151
230
 
152
- useEffect(() => {
153
- if (status === "new") {
154
- setReadOnly(false);
155
- } else {
156
- const editEnabled = usedEntity ? canEditEntity(collection, authController, path, usedEntity ?? null) : false;
157
- if (usedEntity)
158
- setReadOnly(!editEnabled);
159
- }
160
- }, [authController, usedEntity, status]);
231
+ const mainViewVisible = selectedTab === MAIN_TAB_VALUE || Boolean(selectedSecondaryForm);
161
232
 
162
- const onPreSaveHookError = useCallback((e: Error) => {
163
- setSaving(false);
164
- snackbarController.open({
165
- type: "error",
166
- message: "Error before saving: " + e?.message
167
- });
168
- console.error(e);
169
- }, [snackbarController]);
170
-
171
- const onSaveSuccessHookError = useCallback((e: Error) => {
172
- setSaving(false);
173
- snackbarController.open({
174
- type: "error",
175
- message: "Error after saving (entity is saved): " + e?.message
176
- });
177
- console.error(e);
178
- }, [snackbarController]);
233
+ const authController = useAuthController();
179
234
 
180
- const onSaveSuccess = (updatedEntity: Entity<M>, closeAfterSave: boolean) => {
235
+ const customViewsView: React.ReactNode[] | undefined = customViews && resolvedEntityViews
236
+ .filter(e => !e.includeActions)
237
+ .map((customView) => {
181
238
 
182
- setSaving(false);
183
- if (!autoSave)
184
- snackbarController.open({
185
- type: "success",
186
- message: `${collection.singularName ?? collection.name}: Saved correctly`
187
- });
239
+ if (!customView)
240
+ return null;
241
+ const Builder = customView.Builder;
242
+ if (!Builder) {
243
+ console.error("INTERNAL: customView.Builder is not defined");
244
+ return null;
245
+ }
188
246
 
189
- setUsedEntity(updatedEntity);
190
- setStatus("existing");
247
+ if (!entityId) {
248
+ return null;
249
+ }
191
250
 
192
- onValuesAreModified(false);
251
+ const formexStub = createFormexStub<M>(usedEntity?.values ?? {} as M);
252
+ const usedFormContext: FormContext = formContext ?? {
253
+ entityId,
254
+ disabled: false,
255
+ openEntityMode: layout,
256
+ status: status,
257
+ values: usedEntity?.values ?? {},
258
+ setFieldValue: (key: string, value: any) => {
259
+ throw new Error("You can't update values in read only mode");
260
+ },
261
+ save: () => {
262
+ throw new Error("You can't save in read only mode");
263
+ },
264
+ collection: resolveCollection<M>({
265
+ collection,
266
+ path,
267
+ entityId,
268
+ values: usedEntity?.values ?? {},
269
+ previousValues: usedEntity?.values ?? {},
270
+ propertyConfigs: customizationController.propertyConfigs,
271
+ authController
272
+ }),
273
+ path,
274
+ entity: usedEntity,
275
+ savingError: undefined,
276
+ formex: formexStub
277
+ };
278
+
279
+ return <div
280
+ className={cls(defaultBorderMixin,
281
+ "relative flex-1 w-full h-full overflow-auto",
282
+ { "hidden": selectedTab !== customView.key }
283
+ )}
284
+ key={`custom_view_${customView.key}`}
285
+ role="tabpanel">
286
+ <ErrorBoundary>
287
+ {usedFormContext && <Builder
288
+ collection={collection}
289
+ parentCollectionIds={parentCollectionIds}
290
+ entity={usedEntity}
291
+ modifiedValues={usedFormContext?.formex?.values ?? usedEntity?.values}
292
+ formContext={usedFormContext}
293
+ />}
294
+ </ErrorBoundary>
295
+ </div>;
296
+ }).filter(Boolean);
297
+
298
+ const globalLoading = dataLoading && !usedEntity;
299
+
300
+ const jsonView = <div
301
+ className={cls("relative flex-1 h-full overflow-auto w-full",
302
+ { "hidden": selectedTab !== JSON_TAB_VALUE })}
303
+ key={"json_view"}
304
+ role="tabpanel">
305
+ <ErrorBoundary>
306
+ <EntityJsonPreview
307
+ values={formContext?.values ?? entity?.values ?? {}}/>
308
+ </ErrorBoundary>
309
+ </div>;
310
+
311
+ const subCollectionsViews = subcollections && subcollections.map((subcollection) => {
312
+ const subcollectionId = subcollection.id ?? subcollection.path;
313
+ const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollection.path)}` : undefined;
314
+ const newFullIdPath = fullIdPath ? `${fullIdPath}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollectionId)}` : undefined;
315
+
316
+ if (selectedTab !== subcollectionId) return null;
317
+ return (
318
+ <div
319
+ className={"relative flex-1 h-full overflow-auto w-full"}
320
+ key={`subcol_${subcollectionId}`}
321
+ role="tabpanel">
322
+
323
+ {globalLoading && <CircularProgressCenter/>}
324
+
325
+ {!globalLoading &&
326
+ (usedEntity && newFullPath
327
+ ? <EntityCollectionView
328
+ fullPath={newFullPath}
329
+ fullIdPath={newFullIdPath}
330
+ parentCollectionIds={[...parentCollectionIds, collection.id]}
331
+ isSubCollection={true}
332
+ updateUrl={false}
333
+ {...subcollection}
334
+ openEntityMode={layout}/>
335
+ : <div className="flex items-center justify-center w-full h-full p-3">
336
+ <Typography variant={"label"}>
337
+ You need to save your entity before
338
+ adding additional collections
339
+ </Typography>
340
+ </div>)
341
+ }
193
342
 
194
- if (onUpdate)
195
- onUpdate({ entity: updatedEntity });
343
+ </div>
344
+ );
345
+ }).filter(Boolean);
196
346
 
197
- if (closeAfterSave) {
198
- sideDialogContext.setBlocked(false);
199
- sideDialogContext.close(true);
200
- onClose?.();
201
- } else if (status !== "existing") {
202
- sideEntityController.replace({
203
- path,
204
- entityId: updatedEntity.id,
205
- selectedSubPath: selectedTabRef.current,
206
- updateUrl: true,
207
- collection,
347
+ const onSideTabClick = (value: string) => {
348
+ setSelectedTab(value);
349
+ if (status === "existing") {
350
+ onTabChange?.({
351
+ path: fullIdPath ?? path,
352
+ entityId,
353
+ selectedTab: value === MAIN_TAB_VALUE ? undefined : value,
354
+ collection
208
355
  });
209
356
  }
210
-
211
357
  };
212
358
 
213
- const onSaveFailure = useCallback((e: Error) => {
359
+ const entityReadOnlyView = !canEdit && entity ? <div
360
+ className={cls("flex-1 flex flex-row w-full overflow-y-auto justify-center", (canEdit || !mainViewVisible || selectedSecondaryForm) ? "hidden" : "")}>
361
+ <div
362
+ className={cls("relative flex flex-col max-w-4xl lg:max-w-3xl xl:max-w-4xl 2xl:max-w-6xl w-full h-fit")}>
363
+ <Typography className={"mt-16 mb-8 mx-8"} variant={"h4"}>
364
+ {collection.singularName ?? collection.name}
365
+ </Typography>
366
+ <EntityView
367
+ className={"px-8 h-full overflow-auto"}
368
+ entity={entity}
369
+ path={path}
370
+ collection={collection}/>
371
+ <div className="h-16"/>
372
+ </div>
373
+ </div> : null;
374
+
375
+ const entityView = <EntityForm<M>
376
+ fullIdPath={fullIdPath}
377
+ collection={collection}
378
+ path={path}
379
+ entityId={entityId ?? usedEntity?.id}
380
+ onValuesModified={onValuesModified}
381
+ entity={entity}
382
+ initialDirtyValues={cachedDirtyValues}
383
+ openEntityMode={layout}
384
+ forceActionsAtTheBottom={actionsAtTheBottom}
385
+ initialStatus={status}
386
+ className={cls((!mainViewVisible || !canEdit) && !selectedSecondaryForm ? "hidden" : "", formProps?.className)}
387
+ EntityFormActionsComponent={EntityEditViewFormActions}
388
+ disabled={!canEdit}
389
+ {...formProps}
390
+ onEntityChange={(entity) => {
391
+ setUsedEntity(entity);
392
+ formProps?.onEntityChange?.(entity);
393
+ }}
394
+ onStatusChange={(status) => {
395
+ setStatus(status);
396
+ formProps?.onStatusChange?.(status);
397
+ }}
398
+ onFormContextReady={(formContext) => {
399
+ setFormContext(formContext);
400
+ formProps?.onFormContextReady?.(formContext);
401
+ }}
402
+ onSaved={(params) => {
403
+ const res = {
404
+ ...params,
405
+ selectedTab: MAIN_TAB_VALUE === selectedTab ? undefined : selectedTab
406
+ };
407
+ onSaved?.(res);
408
+ formProps?.onSaved?.(res);
409
+ }}
410
+ Builder={selectedSecondaryForm?.Builder}
411
+ />;
412
+
413
+ const subcollectionTabs = subcollections && subcollections.map((subcollection) =>
414
+ <Tab
415
+ className="text-sm min-w-[120px]"
416
+ value={subcollection.id}
417
+ key={`entity_detail_collection_tab_${subcollection.name}`}>
418
+ {subcollection.name}
419
+ </Tab>
420
+ );
214
421
 
215
- setSaving(false);
216
- snackbarController.open({
217
- type: "error",
218
- message: "Error saving: " + e?.message
219
- });
422
+ const customViewTabsStart = resolvedEntityViews.filter(view => view.position === "start")
423
+ .map((view) =>
424
+ <Tab
425
+ className={!view.tabComponent ? "text-sm min-w-[120px]" : undefined}
426
+ value={view.key}
427
+ key={`entity_detail_collection_tab_${view.name}`}>
428
+ {view.tabComponent ?? view.name}
429
+ </Tab>
430
+ );
431
+ const customViewTabsEnd = resolvedEntityViews.filter(view => !view.position || view.position === "end")
432
+ .map((view) =>
433
+ <Tab
434
+ className={!view.tabComponent ? "text-sm min-w-[120px]" : undefined}
435
+ value={view.key}
436
+ key={`entity_detail_collection_tab_${view.name}`}>
437
+ {view.tabComponent ?? view.name}
438
+ </Tab>
439
+ );
220
440
 
221
- console.error("Error saving entity", path, entityId);
222
- console.error(e);
223
- }, [entityId, path, snackbarController]);
224
-
225
- const saveEntity = ({
226
- values,
227
- previousValues,
228
- closeAfterSave,
229
- entityId,
230
- collection,
231
- path
232
- }: {
233
- collection: EntityCollection<M>,
234
- path: string,
235
- entityId: string | undefined,
236
- values: M,
237
- previousValues?: M,
238
- closeAfterSave: boolean,
239
- }) => {
240
- setSaving(true);
241
- saveEntityWithCallbacks({
242
- path,
243
- entityId,
244
- values,
245
- previousValues,
246
- collection,
247
- status,
248
- dataSource,
249
- context,
250
- onSaveSuccess: (updatedEntity: Entity<M>) => onSaveSuccess(updatedEntity, closeAfterSave),
251
- onSaveFailure,
252
- onPreSaveHookError,
253
- onSaveSuccessHookError
254
- }).then();
255
- };
441
+ const shouldShowTopBar = Boolean(barActions) || hasAdditionalViews;
256
442
 
257
- const onSaveEntityRequest = async ({
258
- collection,
259
- path,
260
- entityId,
261
- values,
262
- previousValues,
263
- closeAfterSave,
264
- autoSave
265
- }: EntityFormSaveParams<M>): Promise<void> => {
266
- if (!status)
267
- return;
268
-
269
- if (autoSave) {
270
- setValuesToBeSaved(values);
271
- } else {
272
- saveEntity({
273
- collection,
274
- path,
275
- entityId,
276
- values,
277
- previousValues,
278
- closeAfterSave
279
- });
280
- }
281
- };
443
+ let result = <div className="relative flex flex-col h-full w-full bg-white dark:bg-surface-900">
282
444
 
283
- const resolvedEntityViews = customViews ? customViews
284
- .map(e => resolveEntityView(e, customizationController.entityViews))
285
- .filter(Boolean) as EntityCustomView[]
286
- : [];
445
+ {shouldShowTopBar && <div
446
+ className={cls("h-14 items-center flex overflow-visible overflow-x-scroll w-full no-scrollbar h-14 border-b pl-2 pr-2 pt-1 flex bg-surface-50 dark:bg-surface-900", defaultBorderMixin)}>
287
447
 
288
- const customViewsView: React.ReactNode[] | undefined = customViews && resolvedEntityViews
289
- .map(
290
- (customView, colIndex) => {
291
- if (!customView)
292
- return null;
293
- if (selectedTabRef.current !== customView.key)
294
- return null;
295
- const Builder = customView.Builder;
296
- if (!Builder) {
297
- console.error("customView.Builder is not defined");
298
- return null;
299
- }
300
- return <div
301
- className={cn(defaultBorderMixin,
302
- "relative flex-grow w-full h-full overflow-auto ")}
303
- key={`custom_view_${customView.key}`}
304
- role="tabpanel">
305
- <ErrorBoundary>
306
- {formContext && <Builder
307
- collection={collection}
308
- entity={usedEntity}
309
- modifiedValues={modifiedValues ?? usedEntity?.values}
310
- formContext={formContext}
311
- />}
312
- </ErrorBoundary>
313
- </div>;
314
- }
315
- ).filter(Boolean);
448
+ {barActions}
316
449
 
317
- const globalLoading = (dataLoading && !usedEntity) ||
318
- ((!usedEntity || readOnly === undefined) && (status === "existing" || status === "copy"));
450
+ <div className={"flex-grow"}/>
319
451
 
320
- const loading = globalLoading || saving;
452
+ {pluginActionsTop}
321
453
 
322
- const subCollectionsViews = subcollections && subcollections.map(
323
- (subcollection, colIndex) => {
324
- const subcollectionId = subcollection.id ?? subcollection.path;
325
- const fullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollectionId)}` : undefined;
326
- if (selectedTabRef.current !== subcollectionId)
327
- return null;
328
- return (
329
- <div
330
- className={"relative flex-grow h-full overflow-auto w-full"}
331
- key={`subcol_${subcollectionId}`}
332
- role="tabpanel">
333
-
334
- {loading && <CircularProgressCenter/>}
335
-
336
- {!globalLoading &&
337
- (usedEntity && fullPath
338
- ? <EntityCollectionView
339
- fullPath={fullPath}
340
- parentCollectionIds={[...parentCollectionIds, collection.id]}
341
- isSubCollection={true}
342
- {...subcollection}/>
343
- : <div
344
- className="flex items-center justify-center w-full h-full p-3">
345
- <Typography variant={"label"}>
346
- You need to save your entity before
347
- adding additional collections
348
- </Typography>
349
- </div>)
350
- }
351
-
352
- </div>
353
- );
354
- }
355
- ).filter(Boolean);
454
+ {globalLoading && <div className="self-center">
455
+ <CircularProgress size={"small"}/>
456
+ </div>}
356
457
 
357
- const onDiscard = useCallback(() => {
358
- onValuesAreModified(false);
359
- }, []);
458
+ {hasAdditionalViews && <Tabs
459
+ className={"self-end"}
460
+ value={selectedTab}
461
+ onValueChange={(value) => {
462
+ onSideTabClick(value);
463
+ }}>
360
464
 
361
- const onSideTabClick = (value: string) => {
362
- selectedTabRef.current = value;
363
- sideEntityController.replace({
364
- path,
365
- entityId,
366
- selectedSubPath: value === MAIN_TAB_VALUE ? undefined : value,
367
- updateUrl: true,
368
- collection,
369
- });
370
- };
465
+ {includeJsonView && <Tab
466
+ disabled={!hasAdditionalViews}
467
+ value={JSON_TAB_VALUE}
468
+ className={"text-sm"}>
469
+ <CodeIcon size={"small"}/>
470
+ </Tab>}
371
471
 
372
- const onValuesChanged = useCallback((values?: EntityValues<M>) => {
373
- modifiedValuesRef.current = values;
374
- }, []);
472
+ {customViewTabsStart}
375
473
 
376
- // eslint-disable-next-line n/handle-callback-err
377
- const onIdUpdateError = useCallback((error: any) => {
378
- snackbarController.open({
379
- type: "error",
380
- message: "Error updating id, check the console"
381
- });
382
- }, []);
474
+ <Tab
475
+ disabled={!hasAdditionalViews}
476
+ value={MAIN_TAB_VALUE}
477
+ className={"text-sm min-w-[120px]"}>
478
+ {collection.singularName ?? collection.name}
479
+ </Tab>
383
480
 
384
- const onIdChange = useCallback((id: string) => {
385
- setUsedEntity((prevEntity) => prevEntity
386
- ? {
387
- ...prevEntity,
388
- id
389
- }
390
- : undefined);
391
- }, []);
392
481
 
393
- const onModified = (dirty: boolean) => {
394
- if (!autoSave)
395
- onValuesAreModified(dirty);
396
- }
482
+ {customViewTabsEnd}
397
483
 
398
- function buildForm() {
399
- const plugins = customizationController.plugins;
400
- let form = <EntityForm
401
- status={status}
402
- path={path}
403
- collection={collection}
404
- onEntitySaveRequested={onSaveEntityRequest}
405
- onDiscard={onDiscard}
406
- onValuesChanged={onValuesChanged}
407
- onModified={onModified}
408
- entity={usedEntity}
409
- onIdChange={onIdChange}
410
- onFormContextChange={setFormContext}
411
- hideId={collection.hideIdFromForm}
412
- autoSave={autoSave}
413
- onIdUpdateError={onIdUpdateError}
414
- />;
415
- if (plugins) {
416
- plugins.forEach((plugin: FireCMSPlugin) => {
417
- if (plugin.form?.provider) {
418
- form = (
419
- <plugin.form.provider.Component
420
- status={status}
421
- path={path}
422
- collection={collection}
423
- onDiscard={onDiscard}
424
- onValuesChanged={onValuesChanged}
425
- onModified={onModified}
426
- entity={usedEntity}
427
- context={context}
428
- formContext={formContext}
429
- {...plugin.form.provider.props}>
430
- {form}
431
- </plugin.form.provider.Component>
432
- );
433
- }
434
- });
435
- }
436
- return <ErrorBoundary>{form}</ErrorBoundary>;
437
- }
484
+ {subcollectionTabs}
485
+ </Tabs>}
486
+ </div>}
438
487
 
439
- const form = (readOnly === undefined)
440
- ? <></>
441
- : (!readOnly
442
- ? buildForm()
443
- : (
444
- <>
445
- <Typography
446
- className={"mt-16 mb-8 mx-8"}
447
- variant={"h4"}>{collection.singularName ?? collection.name}
448
- </Typography>
449
- <EntityView
450
- className={"px-12"}
451
- entity={usedEntity as Entity<M>}
452
- path={path}
453
- collection={collection}/>
454
- </>
455
- ));
488
+ {globalLoading
489
+ ? <div className="w-full pt-12 pb-16 px-4 sm:px-8 md:px-10">
490
+ <CircularProgressCenter/>
491
+ </div>
492
+ : <>
493
+ {entityReadOnlyView}
494
+ {entityView}
495
+ </>}
456
496
 
457
- const subcollectionTabs = subcollections && subcollections.map(
458
- (subcollection) =>
459
- <Tab
460
- className="text-sm min-w-[140px]"
461
- value={subcollection.id}
462
- key={`entity_detail_collection_tab_${subcollection.name}`}>
463
- {subcollection.name}
464
- </Tab>
465
- );
497
+ {jsonView}
466
498
 
467
- const customViewTabs = resolvedEntityViews.map(
468
- (view) =>
499
+ {customViewsView}
469
500
 
470
- <Tab
471
- className="text-sm min-w-[140px]"
472
- value={view.key}
473
- key={`entity_detail_collection_tab_${view.name}`}>
474
- {view.name}
475
- </Tab>
476
- );
501
+ {subCollectionsViews}
477
502
 
478
- return (
479
- <div
480
- className="flex flex-col h-full w-full transition-width duration-250 ease-in-out">
481
- {
482
- <>
483
-
484
- <div
485
- className={cn(defaultBorderMixin, "no-scrollbar border-b pl-2 pr-2 pt-1 flex items-end overflow-scroll bg-gray-50 dark:bg-gray-950")}>
486
-
487
- <div
488
- className="pb-1 self-center">
489
- <IconButton
490
- onClick={() => {
491
- onClose?.();
492
- return sideDialogContext.close(false);
493
- }}
494
- size="large">
495
- <CloseIcon/>
496
- </IconButton>
497
- </div>
498
-
499
- <div className={"flex-grow"}/>
500
-
501
- {globalLoading && <div
502
- className="self-center">
503
- <CircularProgress size={"small"}/>
504
- </div>}
505
-
506
- <Tabs
507
- value={selectedTabRef.current}
508
- onValueChange={(value) => {
509
- onSideTabClick(value);
510
- }}
511
- className="pl-4 pr-4 pt-0">
512
-
513
- <Tab
514
- disabled={!hasAdditionalViews}
515
- value={MAIN_TAB_VALUE}
516
- className={`${
517
- !hasAdditionalViews ? "hidden" : ""
518
- } text-sm min-w-[140px]`}
519
- >{collection.singularName ?? collection.name}</Tab>
520
-
521
- {customViewTabs}
522
-
523
- {subcollectionTabs}
524
- </Tabs>
525
-
526
- </div>
527
-
528
- <div
529
- className={"flex-grow h-full flex overflow-auto flex-row w-full "}
530
- style={{
531
- // width: `calc(${ADDITIONAL_TAB_WIDTH} + ${resolvedFormWidth})`,
532
- // maxWidth: "100%",
533
- // [`@media (max-width: ${resolvedFormWidth})`]: {
534
- // width: resolvedFormWidth
535
- // }
536
- }}>
537
-
538
- <div
539
- role="tabpanel"
540
- hidden={!mainViewVisible}
541
- id={`form_${path}`}
542
- className={" w-full"}>
543
-
544
- {globalLoading
545
- ? <CircularProgressCenter/>
546
- : form}
547
-
548
- </div>
549
-
550
- {customViewsView}
551
-
552
- {subCollectionsViews}
553
-
554
- </div>
555
-
556
- </>
503
+ </div>;
504
+
505
+ if (plugins) {
506
+ plugins.forEach((plugin: FireCMSPlugin) => {
507
+ if (plugin.form?.provider) {
508
+ result = (
509
+ <plugin.form.provider.Component
510
+ status={status}
511
+ path={path}
512
+ collection={collection}
513
+ entity={usedEntity}
514
+ context={context}
515
+ formContext={formContext}
516
+ {...plugin.form.provider.props}>
517
+ {result}
518
+ </plugin.form.provider.Component>
519
+ );
557
520
  }
521
+ });
522
+ }
558
523
 
559
- </div>
560
- );
524
+ return result;
561
525
  }
526
+