@firecms/core 3.0.0-canary.98 → 3.0.0-rc.1

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 (349) hide show
  1. package/README.md +2 -2
  2. package/dist/app/Drawer.d.ts +0 -1
  3. package/dist/app/Scaffold.d.ts +4 -0
  4. package/dist/components/ArrayContainer.d.ts +31 -12
  5. package/dist/components/{DeleteConfirmationDialog.d.ts → ConfirmationDialog.d.ts} +1 -1
  6. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +3 -1
  7. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  8. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +17 -3
  9. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +1 -1
  10. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  11. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +6 -3
  12. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +8 -0
  13. package/dist/components/EntityCollectionView/utils.d.ts +3 -0
  14. package/dist/components/EntityJsonPreview.d.ts +3 -0
  15. package/dist/components/EntityPreview.d.ts +8 -6
  16. package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
  17. package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
  18. package/dist/components/HomePage/NavigationCard.d.ts +3 -1
  19. package/dist/components/HomePage/NavigationCardBinding.d.ts +3 -2
  20. package/dist/components/HomePage/NavigationGroup.d.ts +8 -1
  21. package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
  22. package/dist/components/PropertyConfigBadge.d.ts +2 -1
  23. package/dist/components/PropertyIdCopyTooltip.d.ts +8 -0
  24. package/dist/components/SelectableTable/SelectableTable.d.ts +13 -3
  25. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +1 -1
  26. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  27. package/dist/components/VirtualTable/VirtualTableProps.d.ts +11 -2
  28. package/dist/components/common/default_entity_actions.d.ts +0 -2
  29. package/dist/components/common/index.d.ts +1 -1
  30. package/dist/components/common/useColumnsIds.d.ts +1 -0
  31. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +10 -2
  32. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  33. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  34. package/dist/components/index.d.ts +3 -1
  35. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  36. package/dist/core/DefaultAppBar.d.ts +8 -2
  37. package/dist/core/DrawerNavigationItem.d.ts +2 -1
  38. package/dist/core/EntityEditView.d.ts +40 -22
  39. package/dist/core/EntityEditViewFormActions.d.ts +2 -0
  40. package/dist/core/FireCMS.d.ts +2 -2
  41. package/dist/core/FireCMSRouter.d.ts +4 -0
  42. package/dist/core/NavigationRoutes.d.ts +0 -1
  43. package/dist/core/SideDialogs.d.ts +4 -2
  44. package/dist/core/field_configs.d.ts +1 -1
  45. package/dist/core/index.d.ts +2 -1
  46. package/dist/form/EntityForm.d.ts +50 -0
  47. package/dist/form/EntityFormActions.d.ts +21 -0
  48. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  49. package/dist/form/components/FormEntry.d.ts +6 -0
  50. package/dist/form/components/FormLayout.d.ts +5 -0
  51. package/dist/form/components/LabelWithIcon.d.ts +1 -1
  52. package/dist/form/components/LabelWithIconAndTooltip.d.ts +15 -0
  53. package/dist/form/components/index.d.ts +3 -1
  54. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  55. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  56. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  57. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  58. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  59. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +11 -0
  60. package/dist/form/field_bindings/{MultiSelectBinding.d.ts → MultiSelectFieldBinding.d.ts} +1 -1
  61. package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
  62. package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
  63. package/dist/form/field_bindings/ReferenceFieldBinding.d.ts +2 -2
  64. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  65. package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
  66. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -10
  67. package/dist/form/field_bindings/SwitchFieldBinding.d.ts +1 -2
  68. package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
  69. package/dist/form/index.d.ts +17 -16
  70. package/dist/form/useClearRestoreValue.d.ts +2 -2
  71. package/dist/hooks/data/delete.d.ts +4 -4
  72. package/dist/hooks/data/save.d.ts +3 -3
  73. package/dist/hooks/data/useCollectionFetch.d.ts +1 -1
  74. package/dist/hooks/data/useEntityFetch.d.ts +4 -3
  75. package/dist/hooks/useAuthController.d.ts +1 -1
  76. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  77. package/dist/hooks/useBuildNavigationController.d.ts +57 -12
  78. package/dist/hooks/useFireCMSContext.d.ts +1 -1
  79. package/dist/hooks/useModeController.d.ts +1 -2
  80. package/dist/hooks/useProjectLog.d.ts +7 -1
  81. package/dist/hooks/useResolvedNavigationFrom.d.ts +3 -3
  82. package/dist/hooks/useValidateAuthenticator.d.ts +3 -3
  83. package/dist/index.es.js +20108 -14471
  84. package/dist/index.es.js.map +1 -1
  85. package/dist/index.umd.js +20039 -14407
  86. package/dist/index.umd.js.map +1 -1
  87. package/dist/internal/useBuildDataSource.d.ts +3 -2
  88. package/dist/internal/useBuildSideEntityController.d.ts +3 -3
  89. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  90. package/dist/preview/PropertyPreviewProps.d.ts +1 -1
  91. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  92. package/dist/preview/components/ReferencePreview.d.ts +2 -2
  93. package/dist/preview/util.d.ts +3 -3
  94. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  95. package/dist/routes/FireCMSRoute.d.ts +1 -0
  96. package/dist/routes/HomePageRoute.d.ts +3 -0
  97. package/dist/types/analytics.d.ts +1 -1
  98. package/dist/types/auth.d.ts +7 -9
  99. package/dist/types/collections.d.ts +86 -25
  100. package/dist/types/customization_controller.d.ts +8 -0
  101. package/dist/types/datasource.d.ts +19 -17
  102. package/dist/types/dialogs_controller.d.ts +7 -3
  103. package/dist/types/entities.d.ts +2 -1
  104. package/dist/types/entity_actions.d.ts +58 -8
  105. package/dist/types/entity_callbacks.d.ts +16 -16
  106. package/dist/types/entity_overrides.d.ts +2 -2
  107. package/dist/types/export_import.d.ts +4 -4
  108. package/dist/types/fields.d.ts +43 -17
  109. package/dist/types/firecms.d.ts +16 -3
  110. package/dist/types/firecms_context.d.ts +1 -1
  111. package/dist/types/navigation.d.ts +60 -17
  112. package/dist/types/permissions.d.ts +4 -4
  113. package/dist/types/plugins.d.ts +42 -9
  114. package/dist/types/properties.d.ts +65 -22
  115. package/dist/types/property_config.d.ts +1 -3
  116. package/dist/types/roles.d.ts +3 -0
  117. package/dist/types/side_dialogs_controller.d.ts +10 -0
  118. package/dist/types/side_entity_controller.d.ts +14 -1
  119. package/dist/types/storage.d.ts +75 -0
  120. package/dist/types/user.d.ts +1 -0
  121. package/dist/util/builders.d.ts +3 -3
  122. package/dist/util/callbacks.d.ts +2 -0
  123. package/dist/util/createFormexStub.d.ts +2 -0
  124. package/dist/util/entities.d.ts +2 -2
  125. package/dist/util/entity_actions.d.ts +2 -0
  126. package/dist/util/entity_cache.d.ts +23 -0
  127. package/dist/util/icon_synonyms.d.ts +0 -1
  128. package/dist/util/icons.d.ts +5 -2
  129. package/dist/util/index.d.ts +3 -0
  130. package/dist/util/navigation_from_path.d.ts +10 -1
  131. package/dist/util/navigation_utils.d.ts +13 -1
  132. package/dist/util/objects.d.ts +2 -1
  133. package/dist/util/permissions.d.ts +4 -4
  134. package/dist/util/property_utils.d.ts +4 -4
  135. package/dist/util/references.d.ts +2 -2
  136. package/dist/util/resolutions.d.ts +30 -6
  137. package/dist/util/storage.d.ts +1 -1
  138. package/dist/util/useStorageUploadController.d.ts +2 -2
  139. package/package.json +133 -125
  140. package/src/app/Drawer.tsx +0 -1
  141. package/src/app/Scaffold.tsx +33 -29
  142. package/src/components/ArrayContainer.tsx +447 -229
  143. package/src/components/CircularProgressCenter.tsx +1 -1
  144. package/src/components/ClearFilterSortButton.tsx +1 -1
  145. package/src/components/{DeleteConfirmationDialog.tsx → ConfirmationDialog.tsx} +12 -11
  146. package/src/components/DeleteEntityDialog.tsx +13 -20
  147. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +59 -25
  148. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +23 -17
  149. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +20 -3
  150. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +35 -9
  151. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +21 -16
  152. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +6 -12
  153. package/src/components/EntityCollectionTable/index.tsx +1 -1
  154. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +6 -6
  155. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +35 -26
  156. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +20 -8
  157. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +132 -101
  158. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
  159. package/src/components/EntityCollectionView/EntityCollectionView.tsx +178 -85
  160. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +7 -4
  161. package/src/components/EntityCollectionView/useSelectionController.tsx +5 -4
  162. package/src/components/EntityCollectionView/utils.ts +19 -0
  163. package/src/components/EntityJsonPreview.tsx +66 -0
  164. package/src/components/EntityPreview.tsx +75 -57
  165. package/src/components/EntityView.tsx +8 -5
  166. package/src/components/ErrorView.tsx +3 -3
  167. package/src/components/FireCMSLogo.tsx +7 -51
  168. package/src/components/HomePage/DefaultHomePage.tsx +522 -160
  169. package/src/components/HomePage/FavouritesView.tsx +9 -14
  170. package/src/components/HomePage/HomePageDnD.tsx +642 -0
  171. package/src/components/HomePage/NavigationCard.tsx +47 -38
  172. package/src/components/HomePage/NavigationCardBinding.tsx +16 -15
  173. package/src/components/HomePage/NavigationGroup.tsx +144 -30
  174. package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
  175. package/src/components/HomePage/SmallNavigationCard.tsx +1 -2
  176. package/src/components/NotFoundPage.tsx +2 -2
  177. package/src/components/PropertyConfigBadge.tsx +9 -3
  178. package/src/components/PropertyIdCopyTooltip.tsx +47 -0
  179. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +22 -13
  180. package/src/components/SearchIconsView.tsx +2 -2
  181. package/src/components/SelectableTable/SelectableTable.tsx +154 -142
  182. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +4 -2
  183. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +10 -8
  184. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +59 -10
  185. package/src/components/UnsavedChangesDialog.tsx +46 -0
  186. package/src/components/VirtualTable/VirtualTable.tsx +65 -44
  187. package/src/components/VirtualTable/VirtualTableCell.tsx +0 -8
  188. package/src/components/VirtualTable/VirtualTableHeader.tsx +8 -8
  189. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +1 -1
  190. package/src/components/VirtualTable/VirtualTableProps.tsx +12 -2
  191. package/src/components/VirtualTable/VirtualTableRow.tsx +1 -1
  192. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +4 -4
  193. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +2 -2
  194. package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +2 -1
  195. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +16 -28
  196. package/src/components/common/default_entity_actions.tsx +62 -42
  197. package/src/components/common/index.ts +1 -1
  198. package/src/components/common/useColumnsIds.tsx +1 -1
  199. package/src/components/common/useDataSourceTableController.tsx +420 -0
  200. package/src/components/common/useDebouncedCallback.tsx +20 -0
  201. package/src/components/common/useScrollRestoration.tsx +68 -0
  202. package/src/components/common/useTableSearchHelper.ts +1 -0
  203. package/src/components/index.tsx +4 -1
  204. package/src/contexts/BreacrumbsContext.tsx +38 -0
  205. package/src/contexts/DialogsProvider.tsx +3 -2
  206. package/src/contexts/ModeController.tsx +1 -3
  207. package/src/contexts/SnackbarProvider.tsx +2 -0
  208. package/src/core/DefaultAppBar.tsx +124 -85
  209. package/src/core/DefaultDrawer.tsx +30 -22
  210. package/src/core/DrawerNavigationItem.tsx +32 -28
  211. package/src/core/EntityEditView.tsx +388 -995
  212. package/src/core/EntityEditViewFormActions.tsx +329 -0
  213. package/src/core/EntitySidePanel.tsx +88 -20
  214. package/src/core/FireCMS.tsx +46 -25
  215. package/src/core/FireCMSRouter.tsx +17 -0
  216. package/src/core/NavigationRoutes.tsx +23 -32
  217. package/src/core/SideDialogs.tsx +22 -12
  218. package/src/core/field_configs.tsx +24 -10
  219. package/src/core/index.tsx +4 -2
  220. package/src/form/EntityForm.tsx +814 -0
  221. package/src/form/EntityFormActions.tsx +211 -0
  222. package/src/form/PropertyFieldBinding.tsx +55 -41
  223. package/src/form/components/CustomIdField.tsx +9 -3
  224. package/src/form/components/FieldHelperText.tsx +1 -1
  225. package/src/form/components/FormEntry.tsx +22 -0
  226. package/src/form/components/FormLayout.tsx +16 -0
  227. package/src/form/components/LabelWithIcon.tsx +30 -19
  228. package/src/form/components/LabelWithIconAndTooltip.tsx +28 -0
  229. package/src/form/components/StorageItemPreview.tsx +5 -4
  230. package/src/form/components/StorageUploadProgress.tsx +2 -3
  231. package/src/form/components/index.tsx +3 -1
  232. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +30 -18
  233. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +47 -36
  234. package/src/form/field_bindings/BlockFieldBinding.tsx +55 -33
  235. package/src/form/field_bindings/DateTimeFieldBinding.tsx +18 -14
  236. package/src/form/field_bindings/KeyValueFieldBinding.tsx +19 -15
  237. package/src/form/field_bindings/MapFieldBinding.tsx +72 -62
  238. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +159 -0
  239. package/src/form/field_bindings/{MultiSelectBinding.tsx → MultiSelectFieldBinding.tsx} +26 -21
  240. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +10 -8
  241. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
  242. package/src/form/field_bindings/ReferenceFieldBinding.tsx +28 -19
  243. package/src/form/field_bindings/RepeatFieldBinding.tsx +56 -32
  244. package/src/form/field_bindings/SelectFieldBinding.tsx +22 -13
  245. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +247 -168
  246. package/src/form/field_bindings/SwitchFieldBinding.tsx +29 -24
  247. package/src/form/field_bindings/TextFieldBinding.tsx +28 -24
  248. package/src/form/index.tsx +17 -37
  249. package/src/form/useClearRestoreValue.tsx +2 -2
  250. package/src/form/validation.ts +12 -6
  251. package/src/hooks/data/delete.ts +6 -5
  252. package/src/hooks/data/save.ts +26 -35
  253. package/src/hooks/data/useCollectionFetch.tsx +3 -3
  254. package/src/hooks/data/useDataSource.tsx +10 -2
  255. package/src/hooks/data/useEntityFetch.tsx +10 -6
  256. package/src/hooks/useAuthController.tsx +1 -1
  257. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  258. package/src/hooks/useBrowserTitleAndIcon.tsx +1 -1
  259. package/src/hooks/useBuildModeController.tsx +15 -28
  260. package/src/hooks/useBuildNavigationController.tsx +386 -124
  261. package/src/hooks/useFireCMSContext.tsx +3 -33
  262. package/src/hooks/useLargeLayout.tsx +0 -35
  263. package/src/hooks/useModeController.tsx +1 -2
  264. package/src/hooks/useProjectLog.tsx +16 -5
  265. package/src/hooks/useResolvedNavigationFrom.tsx +9 -11
  266. package/src/hooks/useValidateAuthenticator.tsx +3 -3
  267. package/src/internal/useBuildDataSource.ts +67 -80
  268. package/src/internal/useBuildSideDialogsController.tsx +4 -2
  269. package/src/internal/useBuildSideEntityController.tsx +149 -86
  270. package/src/internal/useUnsavedChangesDialog.tsx +127 -91
  271. package/src/preview/PropertyPreview.tsx +28 -12
  272. package/src/preview/PropertyPreviewProps.tsx +1 -1
  273. package/src/preview/components/BooleanPreview.tsx +1 -1
  274. package/src/preview/components/EmptyValue.tsx +1 -1
  275. package/src/preview/components/EnumValuesChip.tsx +1 -1
  276. package/src/preview/components/ImagePreview.tsx +10 -9
  277. package/src/preview/components/ReferencePreview.tsx +6 -16
  278. package/src/preview/components/UrlComponentPreview.tsx +20 -21
  279. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +6 -5
  280. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +5 -4
  281. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +5 -3
  282. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -3
  283. package/src/preview/property_previews/ArrayOneOfPreview.tsx +6 -4
  284. package/src/preview/property_previews/ArrayPropertyPreview.tsx +5 -3
  285. package/src/preview/property_previews/MapPropertyPreview.tsx +7 -6
  286. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +13 -13
  287. package/src/preview/property_previews/StringPropertyPreview.tsx +2 -2
  288. package/src/preview/util.ts +10 -10
  289. package/src/routes/CustomCMSRoute.tsx +21 -0
  290. package/src/routes/FireCMSRoute.tsx +246 -0
  291. package/src/routes/HomePageRoute.tsx +17 -0
  292. package/src/types/analytics.ts +3 -0
  293. package/src/types/auth.tsx +8 -12
  294. package/src/types/collections.ts +101 -28
  295. package/src/types/customization_controller.tsx +9 -0
  296. package/src/types/datasource.ts +21 -20
  297. package/src/types/dialogs_controller.tsx +7 -3
  298. package/src/types/entities.ts +3 -1
  299. package/src/types/entity_actions.tsx +71 -8
  300. package/src/types/entity_callbacks.ts +18 -18
  301. package/src/types/entity_overrides.tsx +2 -2
  302. package/src/types/export_import.ts +4 -4
  303. package/src/types/fields.tsx +52 -19
  304. package/src/types/firecms.tsx +18 -4
  305. package/src/types/firecms_context.tsx +1 -1
  306. package/src/types/navigation.ts +76 -22
  307. package/src/types/permissions.ts +5 -5
  308. package/src/types/plugins.tsx +50 -9
  309. package/src/types/properties.ts +74 -22
  310. package/src/types/property_config.tsx +1 -2
  311. package/src/types/roles.ts +3 -0
  312. package/src/types/side_dialogs_controller.tsx +15 -0
  313. package/src/types/side_entity_controller.tsx +16 -1
  314. package/src/types/storage.ts +82 -0
  315. package/src/types/user.ts +2 -0
  316. package/src/util/builders.ts +10 -8
  317. package/src/util/callbacks.ts +119 -0
  318. package/src/util/createFormexStub.tsx +62 -0
  319. package/src/util/entities.ts +5 -3
  320. package/src/util/entity_actions.ts +28 -0
  321. package/src/util/entity_cache.ts +204 -0
  322. package/src/util/icon_list.ts +1 -1
  323. package/src/util/icon_synonyms.ts +0 -1
  324. package/src/util/icons.tsx +36 -11
  325. package/src/util/index.ts +3 -0
  326. package/src/util/join_collections.ts +9 -2
  327. package/src/util/make_properties_editable.ts +13 -5
  328. package/src/util/navigation_from_path.ts +33 -12
  329. package/src/util/navigation_utils.ts +135 -19
  330. package/src/util/objects.ts +74 -14
  331. package/src/util/parent_references_from_path.ts +3 -3
  332. package/src/util/permissions.ts +8 -8
  333. package/src/util/property_utils.tsx +17 -6
  334. package/src/util/references.ts +19 -8
  335. package/src/util/resolutions.ts +93 -24
  336. package/src/util/storage.ts +6 -2
  337. package/src/util/useStorageUploadController.tsx +74 -29
  338. package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
  339. package/dist/components/PropertyIdCopyTooltipContent.d.ts +0 -3
  340. package/dist/form/PropertiesForm.d.ts +0 -8
  341. package/dist/form/components/FormikArrayContainer.d.ts +0 -18
  342. package/dist/form/field_bindings/MarkdownFieldBinding.d.ts +0 -9
  343. package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
  344. package/src/components/PropertyIdCopyTooltipContent.tsx +0 -27
  345. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -236
  346. package/src/form/PropertiesForm.tsx +0 -81
  347. package/src/form/components/FormikArrayContainer.tsx +0 -44
  348. package/src/form/field_bindings/MarkdownFieldBinding.tsx +0 -695
  349. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -0,0 +1,814 @@
1
+ import React, { useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from "react";
2
+ import {
3
+ AuthController,
4
+ CMSAnalyticsEvent,
5
+ Entity,
6
+ EntityCollection,
7
+ EntityCustomViewParams,
8
+ EntityStatus,
9
+ EntityValues,
10
+ FormContext,
11
+ PluginFormActionProps,
12
+ PropertyConfig,
13
+ PropertyFieldBindingProps,
14
+ ResolvedEntityCollection
15
+ } from "../types";
16
+ import equal from "react-fast-compare";
17
+
18
+ import { ErrorBoundary, getFormFieldKeys } from "../components";
19
+ import {
20
+ getDefaultValuesFor,
21
+ getEntityTitlePropertyKey,
22
+ getValueInPath,
23
+ isHidden,
24
+ isReadOnly,
25
+ mergeDeep,
26
+ resolveCollection,
27
+ useDebouncedCallback
28
+ } from "../util";
29
+
30
+ import {
31
+ saveEntityWithCallbacks,
32
+ useAuthController,
33
+ useCustomizationController,
34
+ useDataSource,
35
+ useFireCMSContext, useNavigationController,
36
+ useSideEntityController,
37
+ useSnackbarController
38
+ } from "../hooks";
39
+ import { Alert, CheckIcon, Chip, cls, EditIcon, NotesIcon, paperMixin, Tooltip, Typography } from "@firecms/ui";
40
+ import { Formex, FormexController, getIn, setIn, useCreateFormex } from "@firecms/formex";
41
+ import { useAnalyticsController } from "../hooks/useAnalyticsController";
42
+ import { FormEntry, FormLayout, LabelWithIconAndTooltip, PropertyFieldBinding } from "../form";
43
+ import { ValidationError } from "yup";
44
+ import { removeEntityFromCache, saveEntityToCache } from "../util/entity_cache";
45
+ import { CustomIdField } from "../form/components/CustomIdField";
46
+ import { ErrorFocus } from "../form/components/ErrorFocus";
47
+ import { CustomFieldValidator, getYupEntitySchema } from "../form/validation";
48
+ import { EntityFormActions, EntityFormActionsProps } from "./EntityFormActions";
49
+
50
+ export type OnUpdateParams = {
51
+ entity: Entity<any>,
52
+ status: EntityStatus,
53
+ path: string,
54
+ entityId?: string;
55
+ selectedTab?: string;
56
+ collection: EntityCollection<any>
57
+ };
58
+
59
+ export type EntityFormProps<M extends Record<string, any>> = {
60
+ path: string;
61
+ fullIdPath?: string;
62
+ collection: EntityCollection<M>;
63
+ entityId?: string;
64
+ entity?: Entity<M>;
65
+ databaseId?: string;
66
+ onIdChange?: (id: string) => void;
67
+ onValuesModified?: (modified: boolean) => void;
68
+ onSaved?: (params: OnUpdateParams) => void;
69
+ initialDirtyValues?: Partial<M>; // dirty cached entity in memory
70
+ onFormContextReady?: (formContext: FormContext) => void;
71
+ forceActionsAtTheBottom?: boolean;
72
+ className?: string;
73
+ initialStatus: EntityStatus;
74
+ onStatusChange?: (status: EntityStatus) => void;
75
+ onEntityChange?: (entity: Entity<M>) => void;
76
+ formex?: FormexController<M>;
77
+ openEntityMode?: "side_panel" | "full_screen";
78
+ /**
79
+ * If true, the form will be disabled and no actions will be available
80
+ */
81
+ disabled?: boolean;
82
+ /**
83
+ * Include the copy and delete actions in the form
84
+ */
85
+ showDefaultActions?: boolean;
86
+
87
+ /**
88
+ * Display the entity path in the form
89
+ */
90
+ showEntityPath?: boolean;
91
+
92
+ EntityFormActionsComponent?: React.FC<EntityFormActionsProps>;
93
+
94
+ Builder?: React.ComponentType<EntityCustomViewParams<M>>;
95
+
96
+ children?: React.ReactNode;
97
+ };
98
+
99
+ export function EntityForm<M extends Record<string, any>>({
100
+ path,
101
+ fullIdPath,
102
+ entityId: entityIdProp,
103
+ collection,
104
+ onValuesModified,
105
+ onIdChange,
106
+ onSaved,
107
+ entity,
108
+ initialDirtyValues,
109
+ onFormContextReady,
110
+ forceActionsAtTheBottom,
111
+ initialStatus,
112
+ className,
113
+ onStatusChange,
114
+ onEntityChange,
115
+ openEntityMode = "full_screen",
116
+ formex: formexProp,
117
+ disabled: disabledProp,
118
+ Builder,
119
+ EntityFormActionsComponent = EntityFormActions,
120
+ showDefaultActions = true,
121
+ showEntityPath = true,
122
+ children
123
+ }: EntityFormProps<M>) {
124
+
125
+ if (collection.customId && collection.formAutoSave) {
126
+ console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
127
+ }
128
+
129
+
130
+ const sideEntityController = useSideEntityController();
131
+ const navigationController = useNavigationController();
132
+
133
+ const navigateBack = useCallback(() => {
134
+ if (openEntityMode === "side_panel") {
135
+ // If we are in side panel mode, we close the side panel
136
+ sideEntityController.close();
137
+ } else {
138
+ window.history.back();
139
+ }
140
+ }, []);
141
+
142
+ const authController = useAuthController();
143
+ const [status, setStatus] = useState<EntityStatus>(initialStatus);
144
+
145
+ const updateStatus = (status: EntityStatus) => {
146
+ setStatus(status);
147
+ onStatusChange?.(status);
148
+ };
149
+
150
+ const [valuesToBeSaved, setValuesToBeSaved] = useState<EntityValues<M> | undefined>(undefined);
151
+ useDebouncedCallback(valuesToBeSaved, () => {
152
+ if (valuesToBeSaved)
153
+ saveEntity({
154
+ entityId: entityIdProp,
155
+ collection,
156
+ path,
157
+ values: valuesToBeSaved
158
+ });
159
+ }, false, 2000);
160
+
161
+ const dataSource = useDataSource(collection);
162
+ const snackbarController = useSnackbarController();
163
+ const customizationController = useCustomizationController();
164
+ const context = useFireCMSContext();
165
+ const analyticsController = useAnalyticsController();
166
+
167
+ const [underlyingChanges, setUnderlyingChanges] = useState<Partial<EntityValues<M>>>({});
168
+
169
+ const [customIdLoading, setCustomIdLoading] = useState<boolean>(false);
170
+
171
+ const mustSetCustomId: boolean = (status === "new" || status === "copy") &&
172
+ (Boolean(collection.customId) && collection.customId !== "optional");
173
+
174
+ const initialEntityId: string | undefined = useMemo((): string | undefined => {
175
+ if (status === "new" || status === "copy") {
176
+ if (mustSetCustomId) {
177
+ return undefined;
178
+ } else {
179
+ return dataSource.generateEntityId(path, collection);
180
+ }
181
+ } else {
182
+ return entityIdProp;
183
+ }
184
+ }, [entityIdProp, status]);
185
+
186
+ const [entityId, setEntityId] = useState<string | undefined>(initialEntityId);
187
+ const [entityIdError, setEntityIdError] = useState<boolean>(false);
188
+ const [savingError, setSavingError] = useState<Error | undefined>();
189
+
190
+ const autoSave = collection.formAutoSave && !collection.customId;
191
+
192
+ const onSubmit = (values: EntityValues<M>, formexController: FormexController<EntityValues<M>>) => {
193
+
194
+ if (mustSetCustomId && !entityId) {
195
+ console.error("Missing custom Id");
196
+ setEntityIdError(true);
197
+ formexController.setSubmitting(false);
198
+ return;
199
+ }
200
+
201
+ setSavingError(undefined);
202
+ setEntityIdError(false);
203
+
204
+ if (status === "existing") {
205
+ if (!entity?.id) throw Error("Form misconfiguration when saving, no id for existing entity");
206
+ } else if (status === "new" || status === "copy") {
207
+ if (collection.customId) {
208
+ if (collection.customId !== "optional" && !entityId) {
209
+ throw Error("Form misconfiguration when saving, entityId should be set");
210
+ }
211
+ }
212
+ } else {
213
+ throw Error("New FormType added, check EntityForm");
214
+ }
215
+
216
+ return save(values)
217
+ ?.then(_ => {
218
+ formexController.resetForm({
219
+ values,
220
+ submitCount: 0,
221
+ touched: {}
222
+ });
223
+ })
224
+ .finally(() => {
225
+ formexController.setSubmitting(false);
226
+ });
227
+ };
228
+
229
+ const formex: FormexController<M> = formexProp ?? useCreateFormex<M>({
230
+ initialValues: (initialDirtyValues ?? getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs)) as M,
231
+ initialDirty: Boolean(initialDirtyValues),
232
+ onSubmit,
233
+ onReset: () => {
234
+ clearDirtyCache();
235
+ onValuesModified?.(false);
236
+ },
237
+ validation: (values) => {
238
+ return validationSchema?.validate(values, { abortEarly: false })
239
+ .then(() => {
240
+ return {};
241
+ })
242
+ .catch((e: any) => {
243
+ return yupToFormErrors(e);
244
+ });
245
+ }
246
+ });
247
+
248
+ useEffect(() => {
249
+
250
+ const handleKeyDown = (e: KeyboardEvent) => {
251
+ const isUndo = (e.metaKey || e.ctrlKey) && !e.shiftKey && e.key.toLowerCase() === "z";
252
+ const isRedo =
253
+ ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "z") ||
254
+ ((e.metaKey || e.ctrlKey) && !e.shiftKey && e.key.toLowerCase() === "y");
255
+
256
+ if (isUndo && formex.canUndo) {
257
+ e.preventDefault();
258
+ formex.undo();
259
+ } else if (isRedo && formex.canRedo) {
260
+ e.preventDefault();
261
+ formex.redo();
262
+ }
263
+ };
264
+
265
+ window.addEventListener("keydown", handleKeyDown);
266
+ return () => window.removeEventListener("keydown", handleKeyDown);
267
+
268
+ }, [formex]);
269
+
270
+ const resolvedCollection = useMemo(() => resolveCollection<M>({
271
+ collection,
272
+ path,
273
+ entityId,
274
+ values: formex.values,
275
+ previousValues: formex.initialValues,
276
+ propertyConfigs: customizationController.propertyConfigs,
277
+ authController
278
+ }), [collection, path, entityId, formex.values, formex.initialValues, customizationController.propertyConfigs]);
279
+
280
+ const onPreSaveHookError = useCallback((e: Error) => {
281
+ snackbarController.open({
282
+ type: "error",
283
+ message: "Error before saving: " + e?.message
284
+ });
285
+ console.error(e);
286
+ }, [snackbarController]);
287
+
288
+ const onSaveSuccessHookError = useCallback((e: Error) => {
289
+ snackbarController.open({
290
+ type: "error",
291
+ message: "Error after saving (entity is saved): " + e?.message
292
+ });
293
+ console.error(e);
294
+ }, [snackbarController]);
295
+
296
+ function clearDirtyCache() {
297
+ if (status === "new" || status === "copy") {
298
+ removeEntityFromCache(path + "#new");
299
+ } else {
300
+ removeEntityFromCache(path + "/" + entityId);
301
+ }
302
+ }
303
+
304
+ const onSaveSuccess = (updatedEntity: Entity<M>) => {
305
+
306
+ clearDirtyCache();
307
+ onValuesModified?.(false);
308
+ if (!autoSave)
309
+ snackbarController.open({
310
+ type: "success",
311
+ message: `${collection.singularName ?? collection.name}: Saved correctly`
312
+ });
313
+ onEntityChange?.(updatedEntity);
314
+ updateStatus("existing");
315
+ setEntityId(updatedEntity.id);
316
+
317
+ if (onSaved) {
318
+ onSaved({
319
+ entity: updatedEntity,
320
+ status,
321
+ path,
322
+ entityId: updatedEntity.id,
323
+ collection
324
+ });
325
+ }
326
+ };
327
+
328
+ const onSaveFailure = useCallback((e: Error) => {
329
+ snackbarController.open({
330
+ type: "error",
331
+ message: "Error saving: " + e?.message
332
+ });
333
+ console.error("Error saving entity", path, entityId);
334
+ console.error(e);
335
+ }, [entityId, path, snackbarController]);
336
+
337
+ const saveEntity = ({
338
+ values,
339
+ previousValues,
340
+ entityId,
341
+ collection,
342
+ path
343
+ }: {
344
+ collection: EntityCollection<M>,
345
+ path: string,
346
+ entityId: string | undefined,
347
+ values: M,
348
+ previousValues?: M,
349
+ }) => {
350
+ return saveEntityWithCallbacks({
351
+ path,
352
+ entityId,
353
+ values,
354
+ previousValues,
355
+ collection,
356
+ status,
357
+ dataSource,
358
+ context,
359
+ onSaveSuccess,
360
+ onSaveFailure,
361
+ onPreSaveHookError,
362
+ onSaveSuccessHookError
363
+ }).then();
364
+ };
365
+
366
+ type EntityFormSaveParams<M extends Record<string, any>> = {
367
+ collection: ResolvedEntityCollection<M>,
368
+ path: string,
369
+ entityId: string | undefined,
370
+ values: EntityValues<M>,
371
+ previousValues?: EntityValues<M>,
372
+ autoSave: boolean
373
+ };
374
+
375
+ const onSaveEntityRequest = async ({
376
+ collection,
377
+ path,
378
+ entityId,
379
+ values,
380
+ previousValues,
381
+ autoSave
382
+ }: EntityFormSaveParams<M>): Promise<void> => {
383
+ if (!status)
384
+ return;
385
+ if (autoSave) {
386
+ setValuesToBeSaved(values);
387
+ } else {
388
+ return saveEntity({
389
+ collection,
390
+ path,
391
+ entityId,
392
+ values,
393
+ previousValues
394
+ });
395
+ }
396
+ };
397
+
398
+ const lastSavedValues = useRef<EntityValues<M> | undefined>(entity?.values);
399
+ const save = (values: EntityValues<M>): Promise<void> => {
400
+ lastSavedValues.current = values;
401
+ return onSaveEntityRequest({
402
+ collection: resolvedCollection,
403
+ path,
404
+ entityId,
405
+ values,
406
+ previousValues: entity?.values,
407
+ autoSave: autoSave ?? false
408
+ }).then((res) => {
409
+ const eventName: CMSAnalyticsEvent = status === "new"
410
+ ? "new_entity_saved"
411
+ : (status === "copy" ? "entity_copied" : (status === "existing" ? "entity_edited" : "unmapped_event"));
412
+ analyticsController.onAnalyticsEvent?.(eventName, { path });
413
+ }).catch(e => {
414
+ console.error(e);
415
+ setSavingError(e);
416
+ });
417
+ };
418
+
419
+ const disabled = formex.isSubmitting || Boolean(disabledProp);
420
+
421
+ const formContext: FormContext<M> = {
422
+ // @ts-ignore
423
+ setFieldValue: useCallback(formex.setFieldValue, []),
424
+ values: formex.values,
425
+ collection: resolvedCollection,
426
+ entityId: entityId as string,
427
+ path,
428
+ save,
429
+ formex,
430
+ entity,
431
+ savingError,
432
+ status,
433
+ openEntityMode,
434
+ disabled
435
+ };
436
+
437
+ useEffect(() => {
438
+ onFormContextReady?.(formContext);
439
+ }, [formex.version, resolvedCollection, entityId, path]);
440
+
441
+ const onIdUpdateError = useCallback((error: any) => {
442
+ snackbarController.open({
443
+ type: "error",
444
+ message: "Error updating id, check the console"
445
+ });
446
+ }, []);
447
+
448
+ const pluginActions: React.ReactNode[] = [];
449
+ const plugins = customizationController.plugins;
450
+
451
+ const actionsDisabled = disabled || formex.isSubmitting || (status === "existing" && !formex.dirty) || Boolean(disabledProp);
452
+ const parentCollectionIds = navigationController.getParentCollectionIds(path);
453
+
454
+ if (plugins && collection) {
455
+ const actionProps: PluginFormActionProps = {
456
+ entityId,
457
+ parentCollectionIds,
458
+ path,
459
+ status,
460
+ collection,
461
+ context,
462
+ formContext,
463
+ openEntityMode,
464
+ disabled: actionsDisabled,
465
+ };
466
+ pluginActions.push(...plugins.map((plugin) => (
467
+ plugin.form?.Actions
468
+ ? <plugin.form.Actions
469
+ key={`actions_${plugin.key}`} {...actionProps} />
470
+ : null
471
+ )).filter(Boolean));
472
+ }
473
+
474
+ const titlePropertyKey = getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs);
475
+ const title = (formex.values && titlePropertyKey ? getValueInPath(formex.values, titlePropertyKey) : undefined)
476
+ ?? collection.singularName
477
+ ?? collection.name;
478
+
479
+ const onIdUpdate = collection.callbacks?.onIdUpdate;
480
+ const doOnIdUpdate = useCallback(async () => {
481
+ if (onIdUpdate && formex.values && (status === "new" || status === "copy")) {
482
+ setCustomIdLoading(true);
483
+ try {
484
+ const updatedId = await onIdUpdate({
485
+ collection: resolvedCollection,
486
+ path,
487
+ entityId,
488
+ values: formex.values,
489
+ context
490
+ });
491
+ setEntityId(updatedId);
492
+ } catch (e) {
493
+ onIdUpdateError?.(e);
494
+ console.error(e);
495
+ }
496
+ setCustomIdLoading(false);
497
+ }
498
+ }, [entityId, formex.values, status, onIdUpdate, resolvedCollection, path, context, onIdUpdateError]);
499
+
500
+ useEffect(() => {
501
+ doOnIdUpdate();
502
+ }, [doOnIdUpdate]);
503
+
504
+ useEffect(() => {
505
+ if (!autoSave) {
506
+ onValuesModified?.(modified);
507
+ }
508
+ }, [formex.dirty]);
509
+
510
+ const deferredValues = useDeferredValue(formex.values);
511
+ const modified = formex.dirty;
512
+
513
+ const uniqueFieldValidator: CustomFieldValidator = useCallback(({
514
+ name,
515
+ value,
516
+ property
517
+ }) => dataSource.checkUniqueField(path, name, value, entityId, collection),
518
+ [dataSource, path, entityId]);
519
+
520
+ const validationSchema = useMemo(() => entityId
521
+ ? getYupEntitySchema(
522
+ entityId,
523
+ resolvedCollection.properties,
524
+ uniqueFieldValidator)
525
+ : undefined,
526
+ [entityId, resolvedCollection.properties, uniqueFieldValidator]);
527
+
528
+ useEffect(() => {
529
+ const key = (status === "new" || status === "copy") ? path + "#new" : path + "/" + entityId;
530
+ if (modified) {
531
+ saveEntityToCache(key, deferredValues);
532
+ }
533
+ }, [deferredValues, modified, path, entityId, status]);
534
+
535
+ useOnAutoSave(autoSave, formex, lastSavedValues, save);
536
+
537
+ useEffect(() => {
538
+ if (!autoSave && !formex.isSubmitting && underlyingChanges && entity) {
539
+ // we update the form fields from the Firestore data
540
+ // if they were not touched
541
+ Object.entries(underlyingChanges).forEach(([key, value]) => {
542
+ const formValue = formex.values[key];
543
+ if (!equal(value, formValue) && !formex.touched[key]) {
544
+ console.debug("Updated value from the datasource:", key, value);
545
+ formex.setFieldValue(key, value !== undefined ? value : null);
546
+ }
547
+ });
548
+ }
549
+ }, [formex.isSubmitting, autoSave, underlyingChanges, entity, formex.values, formex.touched, formex.setFieldValue]);
550
+
551
+ const formFieldKeys = getFormFieldKeys(resolvedCollection);
552
+
553
+ const formFields = () => {
554
+
555
+ if (Builder) {
556
+ return <Builder
557
+ collection={collection}
558
+ entity={entity}
559
+ modifiedValues={formex.values}
560
+ formContext={formContext}
561
+ />;
562
+ }
563
+ return (
564
+ <FormLayout>
565
+ {formFieldKeys.map((key) => {
566
+ const property = resolvedCollection.properties[key];
567
+ if (property) {
568
+ const underlyingValueHasChanged: boolean =
569
+ !!underlyingChanges &&
570
+ Object.keys(underlyingChanges).includes(key) &&
571
+ formex.touched[key];
572
+ const disabled = disabledProp || (!autoSave && formex.isSubmitting) || isReadOnly(property) || Boolean(property.disabled);
573
+ const hidden = isHidden(property);
574
+ if (hidden) return null;
575
+ const widthPercentage = property.widthPercentage ?? 100;
576
+ const cmsFormFieldProps: PropertyFieldBindingProps<any, M> = {
577
+ propertyKey: key,
578
+ disabled,
579
+ property,
580
+ includeDescription: property.description || property.longDescription,
581
+ underlyingValueHasChanged: underlyingValueHasChanged && !autoSave,
582
+ context: formContext,
583
+ partOfArray: false,
584
+ minimalistView: false,
585
+ autoFocus: false
586
+ };
587
+
588
+ return (
589
+ <FormEntry propertyKey={key}
590
+ widthPercentage={widthPercentage}
591
+ key={`field_${key}`}>
592
+ <PropertyFieldBinding {...cmsFormFieldProps} />
593
+ </FormEntry>
594
+ );
595
+ }
596
+
597
+ const additionalField = resolvedCollection.additionalFields?.find(f => f.key === key);
598
+ if (additionalField && entity) {
599
+ const Builder = additionalField.Builder;
600
+ if (!Builder && !additionalField.value) {
601
+ throw new Error("When using additional fields you need to provide a Builder or a value");
602
+ }
603
+ const child = Builder
604
+ ? <Builder entity={entity} context={context}/>
605
+ : <div className={"w-full"}>
606
+ {additionalField.value?.({
607
+ entity,
608
+ context
609
+ })?.toString()}
610
+ </div>;
611
+
612
+ return (
613
+ <div key={`additional_${key}`} className={"w-full"}>
614
+ <LabelWithIconAndTooltip
615
+ propertyKey={key}
616
+ icon={<NotesIcon size={"small"}/>}
617
+ title={additionalField.name}
618
+ className={"text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>
619
+ <div
620
+ className={cls(paperMixin, "w-full min-h-14 p-4 md:p-6 overflow-x-scroll no-scrollbar")}>
621
+ <ErrorBoundary>
622
+ {child}
623
+ </ErrorBoundary>
624
+ </div>
625
+ </div>
626
+ );
627
+ }
628
+
629
+ console.warn(`Property ${key} not found in collection ${resolvedCollection.name} in properties or additional fields. Skipping.`);
630
+ return null;
631
+ }).filter(Boolean)}
632
+ </FormLayout>
633
+ );
634
+ };
635
+
636
+ const formRef = useRef<HTMLDivElement>(null);
637
+
638
+ const formView = <ErrorBoundary>
639
+ <>
640
+ {!Builder && <div className={"w-full py-2 flex flex-col items-start my-4 lg:my-6"}>
641
+ <Typography
642
+ className={"my-4 flex-grow line-clamp-1 " + (collection.hideIdFromForm ? "mb-6" : "")}
643
+ variant={"h4"}>
644
+ {title ?? collection.singularName ?? collection.name}
645
+ </Typography>
646
+
647
+ {!entity?.values && initialStatus === "existing" &&
648
+ <Alert color={"warning"} size={"small"} outerClassName={"w-full mb-4 text-xs"}>
649
+ This entity does not exist in the database
650
+ </Alert>}
651
+
652
+ {showEntityPath && <Alert color={"base"} outerClassName={"w-full"} size={"small"}>
653
+ <code
654
+ className={"text-xs select-all text-text-secondary dark:text-text-secondary-dark"}>
655
+ {entity?.path ?? path}/{entityId}
656
+ </code>
657
+ </Alert>}
658
+ </div>}
659
+
660
+ {children}
661
+
662
+ {initialEntityId && !entity && initialStatus !== "new" && <Alert color={"info"} size={"small"}>
663
+ This entity does not exist in the database
664
+ </Alert>}
665
+
666
+ {!Builder && !collection.hideIdFromForm &&
667
+ <CustomIdField customId={collection.customId}
668
+ entityId={entityId}
669
+ status={status}
670
+ onChange={setEntityId}
671
+ error={entityIdError}
672
+ loading={customIdLoading}
673
+ entity={entity}/>
674
+ }
675
+
676
+ {entityId && formContext && <>
677
+ <div className="mt-12 flex flex-col gap-8" ref={formRef}>
678
+ {formFields()}
679
+ <ErrorFocus containerRef={formRef}/>
680
+ </div>
681
+ </>}
682
+
683
+ {forceActionsAtTheBottom && <div className="h-16"/>}
684
+ </>
685
+ </ErrorBoundary>;
686
+
687
+ useEffect(() => {
688
+ if (entityId && onIdChange)
689
+ onIdChange(entityId);
690
+ }, [entityId, onIdChange]);
691
+
692
+ if (!resolvedCollection || !path) {
693
+ throw Error("INTERNAL: Collection and path must be defined in form context");
694
+ }
695
+
696
+ const dialogActions = <EntityFormActionsComponent
697
+ collection={resolvedCollection}
698
+ path={path}
699
+ fullPath={path}
700
+ fullIdPath={fullIdPath}
701
+ entity={entity}
702
+ layout={forceActionsAtTheBottom ? "bottom" : "side"}
703
+ savingError={savingError}
704
+ formex={formex}
705
+ disabled={actionsDisabled}
706
+ status={status}
707
+ pluginActions={pluginActions ?? []}
708
+ openEntityMode={openEntityMode}
709
+ showDefaultActions={showDefaultActions}
710
+ navigateBack={navigateBack}
711
+ formContext={formContext}
712
+ />;
713
+
714
+ return (
715
+ <Formex value={formex}>
716
+ <form
717
+ onSubmit={formex.handleSubmit}
718
+ onReset={() => formex.resetForm({
719
+ values: getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs) as M
720
+ })}
721
+ noValidate
722
+ className={cls("flex-1 flex flex-row w-full overflow-y-auto justify-center", className)}>
723
+ <div
724
+ id={`form_${path}`}
725
+ className={cls("relative flex flex-row max-w-4xl lg:max-w-3xl xl:max-w-4xl 2xl:max-w-6xl w-full h-fit")}>
726
+
727
+ <div className={cls("flex flex-col w-full pt-12 pb-16 px-4 sm:px-8 md:px-10")}>
728
+
729
+ {formex.dirty
730
+ ? <Tooltip title={"Local unsaved changes"}
731
+ className={"self-end sticky top-4 z-10"}>
732
+ <Chip size={"small"} colorScheme={"orangeDarker"}>
733
+ <EditIcon size={"smallest"}/>
734
+ </Chip>
735
+ </Tooltip>
736
+ : <Tooltip title={"In sync with the database"}
737
+ className={"self-end sticky top-4 z-10"}>
738
+ <Chip size={"small"}>
739
+ <CheckIcon size={"smallest"}/>
740
+ </Chip>
741
+ </Tooltip>}
742
+
743
+ {formView}
744
+
745
+ </div>
746
+
747
+ </div>
748
+ {dialogActions}
749
+ </form>
750
+
751
+ </Formex>
752
+ );
753
+ }
754
+
755
+ function getInitialEntityValues<M extends object>(
756
+ authController: AuthController,
757
+ collection: EntityCollection,
758
+ path: string,
759
+ status: "new" | "existing" | "copy",
760
+ entity: Entity<M> | undefined,
761
+ propertyConfigs?: Record<string, PropertyConfig>,
762
+ ): Partial<EntityValues<M>> {
763
+ const resolvedCollection = resolveCollection({
764
+ collection,
765
+ path,
766
+ values: entity?.values,
767
+ propertyConfigs,
768
+ authController
769
+ });
770
+ const properties = resolvedCollection.properties;
771
+ if ((status === "existing" || status === "copy") && entity) {
772
+ if (!collection.alwaysApplyDefaultValues) {
773
+ return entity.values ?? getDefaultValuesFor(properties);
774
+ } else {
775
+ const defaultValues = getDefaultValuesFor(properties);
776
+ return mergeDeep(defaultValues, entity.values ?? {});
777
+ }
778
+ } else if (status === "new") {
779
+ return getDefaultValuesFor(properties);
780
+ } else {
781
+ console.error({
782
+ status,
783
+ entity
784
+ });
785
+ throw new Error("Form has not been initialised with the correct parameters");
786
+ }
787
+ }
788
+
789
+ export function yupToFormErrors(yupError: ValidationError): Record<string, any> {
790
+ let errors: Record<string, any> = {};
791
+ if (yupError.inner) {
792
+ if (yupError.inner.length === 0) {
793
+ return setIn(errors, yupError.path!, yupError.message);
794
+ }
795
+ for (const err of yupError.inner) {
796
+ if (!getIn(errors, err.path!)) {
797
+ errors = setIn(errors, err.path!, err.message);
798
+ }
799
+ }
800
+ }
801
+ return errors;
802
+ }
803
+
804
+ function useOnAutoSave(autoSave: undefined | boolean, formex: FormexController<any>, lastSavedValues: any, save: (values: EntityValues<any>) => Promise<void>) {
805
+ if (!autoSave) return;
806
+ useEffect(() => {
807
+ if (autoSave) {
808
+ if (formex.values && !equal(formex.values, lastSavedValues.current)) {
809
+ save(formex.values);
810
+ }
811
+ }
812
+ }, [formex.values]);
813
+ }
814
+