@firecms/core 3.0.0-canary.23 → 3.0.0-canary.232

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