@firecms/core 3.0.0-canary.19 → 3.0.0-canary.190

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