@firecms/core 3.0.0-canary.29 → 3.0.0-canary.290

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 (433) hide show
  1. package/README.md +3 -3
  2. package/dist/app/AppBar.d.ts +12 -0
  3. package/dist/app/Drawer.d.ts +16 -0
  4. package/dist/app/Scaffold.d.ts +34 -0
  5. package/dist/app/index.d.ts +4 -0
  6. package/dist/app/useApp.d.ts +16 -0
  7. package/dist/components/ArrayContainer.d.ts +31 -12
  8. package/dist/components/CircularProgressCenter.d.ts +1 -1
  9. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  10. package/dist/components/{DeleteConfirmationDialog.d.ts → ConfirmationDialog.d.ts} +1 -1
  11. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +14 -13
  12. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  13. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +22 -6
  14. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
  15. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  16. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +3 -1
  17. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  18. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  19. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  20. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +7 -4
  21. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +20 -2
  22. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  23. package/dist/components/EntityCollectionView/utils.d.ts +3 -0
  24. package/dist/components/EntityJsonPreview.d.ts +3 -0
  25. package/dist/components/EntityPreview.d.ts +10 -7
  26. package/dist/components/ErrorView.d.ts +1 -1
  27. package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
  28. package/dist/components/HomePage/HomePageDnD.d.ts +77 -0
  29. package/dist/components/HomePage/NavigationCard.d.ts +3 -1
  30. package/dist/components/HomePage/NavigationCardBinding.d.ts +4 -3
  31. package/dist/components/HomePage/NavigationGroup.d.ts +8 -1
  32. package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
  33. package/dist/components/PropertyCollectionView.d.ts +23 -0
  34. package/dist/components/PropertyConfigBadge.d.ts +2 -1
  35. package/dist/components/PropertyIdCopyTooltip.d.ts +8 -0
  36. package/dist/components/ReferenceWidget.d.ts +3 -1
  37. package/dist/components/SelectableTable/SelectableTable.d.ts +14 -4
  38. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  39. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  40. package/dist/components/UserDisplay.d.ts +7 -0
  41. package/dist/components/VirtualTable/VirtualTableProps.d.ts +24 -12
  42. package/dist/components/VirtualTable/fields/VirtualTableUserSelect.d.ts +12 -0
  43. package/dist/components/VirtualTable/types.d.ts +3 -3
  44. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -3
  45. package/dist/components/common/index.d.ts +2 -1
  46. package/dist/components/common/table_height.d.ts +5 -0
  47. package/dist/components/common/types.d.ts +4 -6
  48. package/dist/components/common/useColumnsIds.d.ts +3 -1
  49. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +13 -2
  50. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  51. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  52. package/dist/components/index.d.ts +5 -2
  53. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  54. package/dist/contexts/InternalUserManagementContext.d.ts +3 -0
  55. package/dist/core/DefaultAppBar.d.ts +29 -0
  56. package/dist/core/DefaultDrawer.d.ts +19 -0
  57. package/dist/core/DrawerNavigationItem.d.ts +10 -0
  58. package/dist/core/EntityEditView.d.ts +49 -11
  59. package/dist/core/EntityEditViewFormActions.d.ts +2 -0
  60. package/dist/core/FireCMS.d.ts +2 -3
  61. package/dist/core/FireCMSRouter.d.ts +4 -0
  62. package/dist/core/NavigationRoutes.d.ts +2 -3
  63. package/dist/core/SideDialogs.d.ts +4 -2
  64. package/dist/core/field_configs.d.ts +1 -1
  65. package/dist/core/index.d.ts +4 -4
  66. package/dist/form/EntityForm.d.ts +40 -64
  67. package/dist/form/EntityFormActions.d.ts +21 -0
  68. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  69. package/dist/form/components/ErrorFocus.d.ts +1 -1
  70. package/dist/form/components/FieldHelperText.d.ts +3 -3
  71. package/dist/form/components/FormEntry.d.ts +6 -0
  72. package/dist/form/components/FormLayout.d.ts +5 -0
  73. package/dist/form/components/LabelWithIcon.d.ts +1 -1
  74. package/dist/form/components/LabelWithIconAndTooltip.d.ts +15 -0
  75. package/dist/form/components/LocalChangesMenu.d.ts +11 -0
  76. package/dist/form/components/StorageItemPreview.d.ts +4 -4
  77. package/dist/form/components/index.d.ts +3 -1
  78. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  79. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  80. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  81. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  82. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  83. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +11 -0
  84. package/dist/form/field_bindings/{MultiSelectBinding.d.ts → MultiSelectFieldBinding.d.ts} +1 -1
  85. package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
  86. package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
  87. package/dist/form/field_bindings/ReferenceFieldBinding.d.ts +2 -2
  88. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  89. package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
  90. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +5 -13
  91. package/dist/form/field_bindings/SwitchFieldBinding.d.ts +1 -2
  92. package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
  93. package/dist/form/field_bindings/UserSelectFieldBinding.d.ts +12 -0
  94. package/dist/form/index.d.ts +18 -18
  95. package/dist/form/useClearRestoreValue.d.ts +2 -2
  96. package/dist/hooks/data/delete.d.ts +4 -4
  97. package/dist/hooks/data/save.d.ts +4 -5
  98. package/dist/hooks/data/useCollectionFetch.d.ts +1 -1
  99. package/dist/hooks/data/useEntityFetch.d.ts +4 -3
  100. package/dist/hooks/index.d.ts +3 -0
  101. package/dist/hooks/useAuthController.d.ts +1 -1
  102. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  103. package/dist/hooks/useBuildNavigationController.d.ts +57 -13
  104. package/dist/hooks/useCollapsedGroups.d.ts +9 -0
  105. package/dist/hooks/useFireCMSContext.d.ts +1 -1
  106. package/dist/hooks/useInternalUserManagementController.d.ts +12 -0
  107. package/dist/hooks/useModeController.d.ts +1 -2
  108. package/dist/hooks/useProjectLog.d.ts +8 -2
  109. package/dist/hooks/useResolvedNavigationFrom.d.ts +3 -3
  110. package/dist/hooks/useValidateAuthenticator.d.ts +4 -8
  111. package/dist/index.d.ts +1 -0
  112. package/dist/index.es.js +24546 -13965
  113. package/dist/index.es.js.map +1 -1
  114. package/dist/index.umd.js +27256 -588
  115. package/dist/index.umd.js.map +1 -1
  116. package/dist/internal/useBuildDataSource.d.ts +3 -17
  117. package/dist/internal/useBuildSideEntityController.d.ts +3 -3
  118. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  119. package/dist/preview/PropertyPreviewProps.d.ts +6 -1
  120. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  121. package/dist/preview/components/ReferencePreview.d.ts +4 -3
  122. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  123. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  124. package/dist/preview/components/UserPreview.d.ts +8 -0
  125. package/dist/preview/index.d.ts +1 -0
  126. package/dist/preview/util.d.ts +3 -3
  127. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  128. package/dist/routes/FireCMSRoute.d.ts +1 -0
  129. package/dist/routes/HomePageRoute.d.ts +3 -0
  130. package/dist/types/analytics.d.ts +1 -1
  131. package/dist/types/auth.d.ts +8 -10
  132. package/dist/types/collections.d.ts +123 -25
  133. package/dist/types/customization_controller.d.ts +8 -0
  134. package/dist/types/datasource.d.ts +52 -36
  135. package/dist/types/dialogs_controller.d.ts +7 -3
  136. package/dist/types/entities.d.ts +12 -3
  137. package/dist/types/entity_actions.d.ts +72 -8
  138. package/dist/types/entity_callbacks.d.ts +16 -16
  139. package/dist/types/entity_overrides.d.ts +2 -2
  140. package/dist/types/export_import.d.ts +4 -4
  141. package/dist/types/fields.d.ts +79 -39
  142. package/dist/types/firecms.d.ts +31 -3
  143. package/dist/types/firecms_context.d.ts +17 -1
  144. package/dist/types/index.d.ts +1 -1
  145. package/dist/types/internal_user_management.d.ts +20 -0
  146. package/dist/types/navigation.d.ts +62 -19
  147. package/dist/types/permissions.d.ts +4 -4
  148. package/dist/types/plugins.d.ts +58 -13
  149. package/dist/types/properties.d.ts +122 -31
  150. package/dist/types/property_config.d.ts +1 -3
  151. package/dist/types/roles.d.ts +3 -0
  152. package/dist/types/side_dialogs_controller.d.ts +10 -0
  153. package/dist/types/side_entity_controller.d.ts +14 -1
  154. package/dist/types/storage.d.ts +75 -0
  155. package/dist/types/user.d.ts +2 -1
  156. package/dist/util/builders.d.ts +3 -3
  157. package/dist/util/callbacks.d.ts +2 -0
  158. package/dist/util/collections.d.ts +1 -0
  159. package/dist/util/createFormexStub.d.ts +2 -0
  160. package/dist/util/entities.d.ts +3 -3
  161. package/dist/util/entity_actions.d.ts +2 -0
  162. package/dist/util/entity_cache.d.ts +28 -0
  163. package/dist/util/icon_list.d.ts +5 -1
  164. package/dist/util/icon_synonyms.d.ts +1 -98
  165. package/dist/util/icons.d.ts +7 -4
  166. package/dist/util/index.d.ts +3 -0
  167. package/dist/util/make_properties_editable.d.ts +1 -2
  168. package/dist/util/navigation_from_path.d.ts +10 -1
  169. package/dist/util/navigation_utils.d.ts +15 -3
  170. package/dist/util/objects.d.ts +3 -1
  171. package/dist/util/permissions.d.ts +4 -4
  172. package/dist/util/plurals.d.ts +0 -2
  173. package/dist/util/property_utils.d.ts +4 -4
  174. package/dist/util/references.d.ts +2 -2
  175. package/dist/util/resolutions.d.ts +42 -17
  176. package/dist/util/storage.d.ts +23 -2
  177. package/dist/util/useStorageUploadController.d.ts +4 -3
  178. package/package.json +70 -53
  179. package/src/app/AppBar.tsx +18 -0
  180. package/src/app/Drawer.tsx +24 -0
  181. package/src/app/Scaffold.tsx +253 -0
  182. package/src/app/index.ts +4 -0
  183. package/src/app/useApp.tsx +32 -0
  184. package/src/components/ArrayContainer.tsx +447 -229
  185. package/src/components/CircularProgressCenter.tsx +2 -2
  186. package/src/components/ClearFilterSortButton.tsx +41 -0
  187. package/src/components/{DeleteConfirmationDialog.tsx → ConfirmationDialog.tsx} +12 -11
  188. package/src/components/DeleteEntityDialog.tsx +13 -20
  189. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +87 -62
  190. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +38 -31
  191. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +30 -9
  192. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +84 -42
  193. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  194. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +30 -16
  195. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +19 -17
  196. package/src/components/EntityCollectionTable/index.tsx +1 -1
  197. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +34 -39
  198. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +49 -36
  199. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +20 -8
  200. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +135 -105
  201. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
  202. package/src/components/EntityCollectionView/EntityCollectionView.tsx +241 -119
  203. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +7 -4
  204. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  205. package/src/components/EntityCollectionView/useSelectionController.tsx +20 -7
  206. package/src/components/EntityCollectionView/utils.ts +19 -0
  207. package/src/components/EntityJsonPreview.tsx +66 -0
  208. package/src/components/EntityPreview.tsx +83 -62
  209. package/src/components/EntityView.tsx +34 -42
  210. package/src/components/ErrorView.tsx +4 -4
  211. package/src/components/FireCMSLogo.tsx +7 -51
  212. package/src/components/HomePage/DefaultHomePage.tsx +516 -158
  213. package/src/components/HomePage/FavouritesView.tsx +9 -14
  214. package/src/components/HomePage/HomePageDnD.tsx +702 -0
  215. package/src/components/HomePage/NavigationCard.tsx +48 -39
  216. package/src/components/HomePage/NavigationCardBinding.tsx +17 -16
  217. package/src/components/HomePage/NavigationGroup.tsx +144 -30
  218. package/src/components/HomePage/RenameGroupDialog.tsx +123 -0
  219. package/src/components/HomePage/SmallNavigationCard.tsx +5 -6
  220. package/src/components/NotFoundPage.tsx +2 -2
  221. package/src/components/PropertyCollectionView.tsx +329 -0
  222. package/src/components/PropertyConfigBadge.tsx +10 -4
  223. package/src/components/PropertyIdCopyTooltip.tsx +47 -0
  224. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +23 -13
  225. package/src/components/ReferenceWidget.tsx +21 -11
  226. package/src/components/SearchIconsView.tsx +10 -7
  227. package/src/components/SelectableTable/SelectableTable.tsx +157 -157
  228. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  229. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +27 -9
  230. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +36 -12
  231. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +92 -24
  232. package/src/components/UnsavedChangesDialog.tsx +46 -0
  233. package/src/components/UserDisplay.tsx +55 -0
  234. package/src/components/VirtualTable/VirtualTable.tsx +105 -51
  235. package/src/components/VirtualTable/VirtualTableCell.tsx +1 -9
  236. package/src/components/VirtualTable/VirtualTableHeader.tsx +10 -10
  237. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  238. package/src/components/VirtualTable/VirtualTableProps.tsx +28 -14
  239. package/src/components/VirtualTable/VirtualTableRow.tsx +5 -6
  240. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +5 -5
  241. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +2 -2
  242. package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +2 -1
  243. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +16 -28
  244. package/src/components/VirtualTable/fields/VirtualTableUserSelect.tsx +99 -0
  245. package/src/components/VirtualTable/types.tsx +2 -3
  246. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +64 -44
  247. package/src/components/common/index.ts +2 -1
  248. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  249. package/src/components/common/types.tsx +4 -6
  250. package/src/components/common/useColumnsIds.tsx +16 -2
  251. package/src/components/common/useDataSourceTableController.tsx +420 -0
  252. package/src/components/common/useDebouncedCallback.tsx +20 -0
  253. package/src/components/common/useScrollRestoration.tsx +68 -0
  254. package/src/components/common/useTableSearchHelper.ts +53 -12
  255. package/src/components/index.tsx +6 -2
  256. package/src/contexts/BreacrumbsContext.tsx +38 -0
  257. package/src/contexts/DialogsProvider.tsx +5 -4
  258. package/src/contexts/InternalUserManagementContext.tsx +4 -0
  259. package/src/contexts/ModeController.tsx +1 -3
  260. package/src/contexts/SnackbarProvider.tsx +2 -0
  261. package/src/core/DefaultAppBar.tsx +219 -0
  262. package/src/core/DefaultDrawer.tsx +185 -0
  263. package/src/core/DrawerNavigationItem.tsx +66 -0
  264. package/src/core/EntityEditView.tsx +447 -469
  265. package/src/core/EntityEditViewFormActions.tsx +344 -0
  266. package/src/core/EntitySidePanel.tsx +96 -23
  267. package/src/core/FireCMS.tsx +85 -60
  268. package/src/core/FireCMSRouter.tsx +17 -0
  269. package/src/core/NavigationRoutes.tsx +28 -38
  270. package/src/core/SideDialogs.tsx +22 -12
  271. package/src/core/field_configs.tsx +41 -14
  272. package/src/core/index.tsx +6 -5
  273. package/src/form/EntityForm.tsx +740 -523
  274. package/src/form/EntityFormActions.tsx +226 -0
  275. package/src/form/PropertyFieldBinding.tsx +88 -41
  276. package/src/form/components/CustomIdField.tsx +9 -3
  277. package/src/form/components/ErrorFocus.tsx +22 -29
  278. package/src/form/components/FieldHelperText.tsx +4 -4
  279. package/src/form/components/FormEntry.tsx +22 -0
  280. package/src/form/components/FormLayout.tsx +16 -0
  281. package/src/form/components/LabelWithIcon.tsx +30 -19
  282. package/src/form/components/LabelWithIconAndTooltip.tsx +28 -0
  283. package/src/form/components/LocalChangesMenu.tsx +144 -0
  284. package/src/form/components/StorageItemPreview.tsx +23 -13
  285. package/src/form/components/StorageUploadProgress.tsx +5 -6
  286. package/src/form/components/index.tsx +3 -1
  287. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +34 -19
  288. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +50 -36
  289. package/src/form/field_bindings/BlockFieldBinding.tsx +56 -33
  290. package/src/form/field_bindings/DateTimeFieldBinding.tsx +18 -14
  291. package/src/form/field_bindings/KeyValueFieldBinding.tsx +61 -52
  292. package/src/form/field_bindings/MapFieldBinding.tsx +73 -55
  293. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +159 -0
  294. package/src/form/field_bindings/{MultiSelectBinding.tsx → MultiSelectFieldBinding.tsx} +26 -21
  295. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +11 -16
  296. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
  297. package/src/form/field_bindings/ReferenceFieldBinding.tsx +42 -31
  298. package/src/form/field_bindings/RepeatFieldBinding.tsx +62 -35
  299. package/src/form/field_bindings/SelectFieldBinding.tsx +24 -15
  300. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +257 -199
  301. package/src/form/field_bindings/SwitchFieldBinding.tsx +29 -24
  302. package/src/form/field_bindings/TextFieldBinding.tsx +28 -24
  303. package/src/form/field_bindings/UserSelectFieldBinding.tsx +94 -0
  304. package/src/form/index.tsx +21 -37
  305. package/src/form/useClearRestoreValue.tsx +2 -2
  306. package/src/form/validation.ts +13 -23
  307. package/src/hooks/data/delete.ts +6 -5
  308. package/src/hooks/data/save.ts +26 -33
  309. package/src/hooks/data/useCollectionFetch.tsx +3 -3
  310. package/src/hooks/data/useDataSource.tsx +11 -3
  311. package/src/hooks/data/useEntityFetch.tsx +10 -6
  312. package/src/hooks/index.tsx +4 -0
  313. package/src/hooks/useAuthController.tsx +1 -1
  314. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  315. package/src/hooks/useBrowserTitleAndIcon.tsx +1 -1
  316. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  317. package/src/hooks/useBuildModeController.tsx +22 -29
  318. package/src/hooks/useBuildNavigationController.tsx +515 -121
  319. package/src/hooks/useCollapsedGroups.ts +64 -0
  320. package/src/hooks/useFireCMSContext.tsx +9 -35
  321. package/src/hooks/useInternalUserManagementController.tsx +16 -0
  322. package/src/hooks/useLargeLayout.tsx +0 -35
  323. package/src/hooks/useModeController.tsx +1 -2
  324. package/src/hooks/useProjectLog.tsx +32 -10
  325. package/src/hooks/useResolvedNavigationFrom.tsx +10 -12
  326. package/src/hooks/useValidateAuthenticator.tsx +17 -37
  327. package/src/index.ts +1 -0
  328. package/src/internal/useBuildDataSource.ts +79 -85
  329. package/src/internal/useBuildSideDialogsController.tsx +4 -2
  330. package/src/internal/useBuildSideEntityController.tsx +204 -77
  331. package/src/internal/useUnsavedChangesDialog.tsx +127 -91
  332. package/src/preview/PropertyPreview.tsx +42 -25
  333. package/src/preview/PropertyPreviewProps.tsx +7 -1
  334. package/src/preview/components/BooleanPreview.tsx +2 -2
  335. package/src/preview/components/EmptyValue.tsx +1 -1
  336. package/src/preview/components/EnumValuesChip.tsx +2 -2
  337. package/src/preview/components/ImagePreview.tsx +26 -37
  338. package/src/preview/components/ReferencePreview.tsx +30 -38
  339. package/src/preview/components/StorageThumbnail.tsx +5 -1
  340. package/src/preview/components/UrlComponentPreview.tsx +60 -28
  341. package/src/preview/components/UserPreview.tsx +27 -0
  342. package/src/preview/index.ts +1 -0
  343. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +6 -6
  344. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +7 -5
  345. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +5 -4
  346. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -4
  347. package/src/preview/property_previews/ArrayOneOfPreview.tsx +7 -6
  348. package/src/preview/property_previews/ArrayPropertyPreview.tsx +8 -7
  349. package/src/preview/property_previews/MapPropertyPreview.tsx +14 -13
  350. package/src/preview/property_previews/NumberPropertyPreview.tsx +2 -2
  351. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +13 -13
  352. package/src/preview/property_previews/StringPropertyPreview.tsx +3 -3
  353. package/src/preview/util.ts +10 -10
  354. package/src/routes/CustomCMSRoute.tsx +21 -0
  355. package/src/routes/FireCMSRoute.tsx +246 -0
  356. package/src/routes/HomePageRoute.tsx +17 -0
  357. package/src/types/analytics.ts +3 -0
  358. package/src/types/auth.tsx +9 -13
  359. package/src/types/collections.ts +146 -30
  360. package/src/types/customization_controller.tsx +9 -1
  361. package/src/types/datasource.ts +61 -43
  362. package/src/types/dialogs_controller.tsx +7 -3
  363. package/src/types/entities.ts +19 -3
  364. package/src/types/entity_actions.tsx +86 -10
  365. package/src/types/entity_callbacks.ts +18 -18
  366. package/src/types/entity_overrides.tsx +2 -2
  367. package/src/types/export_import.ts +4 -4
  368. package/src/types/fields.tsx +91 -42
  369. package/src/types/firecms.tsx +34 -4
  370. package/src/types/firecms_context.tsx +18 -1
  371. package/src/types/index.ts +1 -1
  372. package/src/types/internal_user_management.ts +24 -0
  373. package/src/types/navigation.ts +77 -24
  374. package/src/types/permissions.ts +5 -5
  375. package/src/types/plugins.tsx +69 -15
  376. package/src/types/properties.ts +141 -33
  377. package/src/types/property_config.tsx +2 -2
  378. package/src/types/roles.ts +3 -0
  379. package/src/types/side_dialogs_controller.tsx +15 -0
  380. package/src/types/side_entity_controller.tsx +16 -1
  381. package/src/types/storage.ts +83 -1
  382. package/src/types/user.ts +3 -1
  383. package/src/util/builders.ts +10 -8
  384. package/src/util/callbacks.ts +119 -0
  385. package/src/util/collections.ts +8 -0
  386. package/src/util/createFormexStub.tsx +66 -0
  387. package/src/util/entities.ts +11 -8
  388. package/src/util/entity_actions.ts +28 -0
  389. package/src/util/entity_cache.ts +223 -0
  390. package/src/util/enums.ts +1 -1
  391. package/src/util/icon_list.ts +16 -10
  392. package/src/util/icon_synonyms.ts +3 -100
  393. package/src/util/icons.tsx +36 -11
  394. package/src/util/index.ts +3 -0
  395. package/src/util/join_collections.ts +11 -4
  396. package/src/util/make_properties_editable.ts +5 -19
  397. package/src/util/navigation_from_path.ts +33 -12
  398. package/src/util/navigation_utils.ts +141 -25
  399. package/src/util/objects.ts +128 -33
  400. package/src/util/parent_references_from_path.ts +3 -3
  401. package/src/util/permissions.ts +9 -8
  402. package/src/util/plurals.ts +0 -2
  403. package/src/util/property_utils.tsx +17 -6
  404. package/src/util/references.ts +19 -8
  405. package/src/util/resolutions.ts +122 -48
  406. package/src/util/storage.ts +79 -21
  407. package/src/util/strings.ts +2 -2
  408. package/src/util/useStorageUploadController.tsx +162 -62
  409. package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
  410. package/dist/components/FireCMSAppBar.d.ts +0 -26
  411. package/dist/components/PropertyIdCopyTooltipContent.d.ts +0 -3
  412. package/dist/components/VirtualTable/common.d.ts +0 -2
  413. package/dist/core/Drawer.d.ts +0 -23
  414. package/dist/core/Scaffold.d.ts +0 -55
  415. package/dist/core/SideEntityView.d.ts +0 -7
  416. package/dist/form/components/FormikArrayContainer.d.ts +0 -18
  417. package/dist/form/field_bindings/MarkdownFieldBinding.d.ts +0 -9
  418. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  419. package/dist/internal/useLocaleConfig.d.ts +0 -1
  420. package/dist/types/appcheck.d.ts +0 -26
  421. package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
  422. package/src/components/FireCMSAppBar.tsx +0 -165
  423. package/src/components/PropertyIdCopyTooltipContent.tsx +0 -28
  424. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -225
  425. package/src/core/Drawer.tsx +0 -191
  426. package/src/core/Scaffold.tsx +0 -281
  427. package/src/core/SideEntityView.tsx +0 -38
  428. package/src/form/components/FormikArrayContainer.tsx +0 -44
  429. package/src/form/field_bindings/MarkdownFieldBinding.tsx +0 -695
  430. package/src/internal/useBuildCustomizationController.tsx +0 -5
  431. package/src/internal/useLocaleConfig.tsx +0 -18
  432. package/src/types/appcheck.ts +0 -29
  433. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -1,59 +1,168 @@
1
1
  import { useCallback, useEffect, useRef } from "react";
2
2
  import {
3
+ AuthController,
4
+ CustomizationController,
3
5
  EntityCollection,
4
6
  EntitySidePanelProps,
5
7
  NavigationController,
8
+ ResolvedProperty,
6
9
  SideDialogPanelProps,
7
10
  SideDialogsController,
8
11
  SideEntityController
9
12
  } from "../types";
10
- import { getNavigationEntriesFromPathInternal, NavigationViewInternal } from "../util/navigation_from_path";
13
+ import { getNavigationEntriesFromPath, NavigationViewInternal } from "../util/navigation_from_path";
11
14
  import { useLocation } from "react-router-dom";
12
- import { removeInitialAndTrailingSlashes, resolveDefaultSelectedView } from "../util";
15
+ import {
16
+ removeInitialAndTrailingSlashes,
17
+ resolveCollection,
18
+ resolveDefaultSelectedView,
19
+ resolvedSelectedEntityView
20
+ } from "../util";
13
21
  import { ADDITIONAL_TAB_WIDTH, CONTAINER_FULL_WIDTH, FORM_CONTAINER_WIDTH } from "./common";
14
- import { useLargeLayout } from "../hooks";
22
+ import { useCustomizationController, useLargeLayout } from "../hooks";
15
23
  import { EntitySidePanel } from "../core/EntitySidePanel";
24
+ import { JSON_TAB_VALUE } from "../core/EntityEditView";
16
25
 
17
- const NEW_URL_HASH = "new";
26
+ const NEW_URL_HASH = "new_side";
27
+ const SIDE_URL_HASH = "side";
18
28
 
19
- export function getEntityViewWidth(props: EntitySidePanelProps<any>, small: boolean): string {
29
+ export function getEntityViewWidth(props: EntitySidePanelProps<any>, small: boolean, customizationController: CustomizationController, authController: AuthController): string {
20
30
  if (small) return CONTAINER_FULL_WIDTH;
21
- const mainViewSelected = !props.selectedSubPath;
22
- const resolvedWidth: string | undefined = typeof props.width === "number" ? `${props.width}px` : props.width;
23
- return !mainViewSelected ? `calc(${ADDITIONAL_TAB_WIDTH} + ${resolvedWidth ?? FORM_CONTAINER_WIDTH})` : resolvedWidth ?? FORM_CONTAINER_WIDTH
31
+
32
+ const {
33
+ selectedSecondaryForm
34
+ } = resolvedSelectedEntityView(props.collection?.entityViews, customizationController, props.selectedTab);
35
+
36
+ const shouldUseSmallLayout = !props.selectedTab || props.selectedTab === JSON_TAB_VALUE || props.selectedTab === "__history" || Boolean(selectedSecondaryForm);
37
+
38
+ let resolvedWidth: string | undefined;
39
+ if (props.width) {
40
+ resolvedWidth = typeof props.width === "number" ? `${props.width}px` : props.width;
41
+ } else if (props.collection?.sideDialogWidth) {
42
+ resolvedWidth = typeof props.collection.sideDialogWidth === "number" ? `${props.collection.sideDialogWidth}px` : props.collection.sideDialogWidth;
43
+ }
44
+
45
+ if (!shouldUseSmallLayout) {
46
+ return `calc(${ADDITIONAL_TAB_WIDTH} + ${resolvedWidth ?? FORM_CONTAINER_WIDTH})`
47
+ } else {
48
+ if (resolvedWidth) {
49
+ return resolvedWidth
50
+ } else if (!props.collection) {
51
+ return FORM_CONTAINER_WIDTH;
52
+ } else {
53
+ return calculateCollectionDesiredWidth(props.collection, authController);
54
+ }
55
+ }
56
+ }
57
+
58
+ const collectionViewWidthCache: { [key: string]: string } = {};
59
+
60
+ function calculateCollectionDesiredWidth(collection: EntityCollection<any>, authController: AuthController): string {
61
+ if (collectionViewWidthCache[collection.id]) {
62
+ return collectionViewWidthCache[collection.id];
63
+ }
64
+ const resolvedCollection = resolveCollection({
65
+ collection,
66
+ path: "__ignored",
67
+ ignoreMissingFields: true,
68
+ authController
69
+ });
70
+
71
+ let result = FORM_CONTAINER_WIDTH
72
+ if (resolvedCollection?.properties) {
73
+ const values = Object.values(resolvedCollection.properties).map((p: ResolvedProperty) => getNestedPropertiesDepth(p));
74
+ const maxDepth = Math.max(...values);
75
+ if (maxDepth < 3) {
76
+ result = FORM_CONTAINER_WIDTH;
77
+ } else {
78
+ result = 768 + 32 * (maxDepth - 2) + "px";
79
+ }
80
+ }
81
+ collectionViewWidthCache[collection.id] = result;
82
+ return result;
83
+ }
84
+
85
+ function getNestedPropertiesDepth(property: ResolvedProperty, accumulator: number = 0): number {
86
+ if (property.dataType === "map" && property.properties) {
87
+ const values = Object.values(property.properties).flatMap((property) => getNestedPropertiesDepth(property, accumulator + 1));
88
+ return Math.max(...values);
89
+ } else if (property.dataType === "array" && property.oneOf) {
90
+ return accumulator + 3;
91
+ } else if (property.dataType === "array" && property.of) {
92
+ if (Array.isArray(property.of)) {
93
+ return Math.max(...property.of.map((p) => getNestedPropertiesDepth(p, accumulator + 1)));
94
+ } else {
95
+ return getNestedPropertiesDepth(property.of, accumulator + 1);
96
+ }
97
+ } else {
98
+ return accumulator + 1;
99
+ }
24
100
  }
25
101
 
26
102
  export const useBuildSideEntityController = (navigation: NavigationController,
27
- sideDialogsController: SideDialogsController): SideEntityController => {
103
+ sideDialogsController: SideDialogsController,
104
+ authController: AuthController
105
+ ): SideEntityController => {
28
106
 
29
107
  const location = useLocation();
30
108
  const initialised = useRef<boolean>(false);
109
+ const customizationController = useCustomizationController();
31
110
 
32
111
  const smallLayout = !useLargeLayout();
33
112
 
34
- // only on initialisation, create panels from URL
35
113
  useEffect(() => {
36
- if (!navigation.loading && !initialised.current) {
37
- console.debug("Initialising side entity controller");
38
- if (navigation.isUrlCollectionPath(location.pathname)) {
39
- const newFlag = location.hash === `#${NEW_URL_HASH}`;
114
+
115
+ const newFlag = location.hash === `#${NEW_URL_HASH}`;
116
+ const sideFlag = location.hash === `#${SIDE_URL_HASH}`;
117
+
118
+ if (!navigation.loading) {
119
+ if ((newFlag || sideFlag) && navigation.isUrlCollectionPath(location.pathname)) {
40
120
  const entityOrCollectionPath = navigation.urlPathToDataPath(location.pathname);
41
121
  const panelsFromUrl = buildSidePanelsFromUrl(entityOrCollectionPath, navigation.collections ?? [], newFlag);
42
122
  for (let i = 0; i < panelsFromUrl.length; i++) {
43
- const panel = panelsFromUrl[i];
44
- setTimeout(() => {
45
- if (i === 0)
46
- sideDialogsController.replace(propsToSidePanel(panel, navigation, smallLayout));
47
- else
48
- sideDialogsController.open(propsToSidePanel(panel, navigation, smallLayout))
49
- }, 1);
123
+ const props = panelsFromUrl[i];
124
+ if (i === 0)
125
+ sideDialogsController.replace(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, customizationController, authController));
126
+ else
127
+ sideDialogsController.open(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, customizationController, authController))
50
128
  }
51
- } else {
52
- console.warn("Location path is not a collection path");
53
129
  }
54
130
  initialised.current = true;
55
131
  }
56
- }, [location, navigation, sideDialogsController, smallLayout]);
132
+ }, [navigation.loading]);
133
+
134
+ // sync panels if URL changes with #side
135
+ const currentPanelKeys = sideDialogsController.sidePanels.map(p => p.key);
136
+ useEffect(() => {
137
+ if (initialised.current) {
138
+ const sideFlag = location.hash === `#${SIDE_URL_HASH}`;
139
+ if (sideFlag) {
140
+ const entityOrCollectionPath = navigation.urlPathToDataPath(location.pathname);
141
+ const panelsFromUrl = buildSidePanelsFromUrl(entityOrCollectionPath, navigation.collections ?? [], false);
142
+ // if we have more panels than determined by the url, we ignore the url. We might have references open
143
+ if (panelsFromUrl.length <= currentPanelKeys.length) {
144
+ return;
145
+ }
146
+ const lastPanel = panelsFromUrl[panelsFromUrl.length - 1];
147
+ const panelProps = propsToSidePanel(lastPanel, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, customizationController, authController);
148
+ const lastCurrentPanel = currentPanelKeys.length > 0 ? currentPanelKeys[currentPanelKeys.length - 1] : undefined;
149
+ if (!lastCurrentPanel || lastCurrentPanel !== panelProps.key) {
150
+ sideDialogsController.replace(panelProps);
151
+ }
152
+ }
153
+ }
154
+ }, [location.pathname, location.hash, currentPanelKeys]);
155
+
156
+ // update side panels to match browser size
157
+ useEffect(() => {
158
+ const updatedSidePanels = sideDialogsController.sidePanels.map(sidePanelProps => {
159
+ if (sidePanelProps.additional) {
160
+ return propsToSidePanel(sidePanelProps.additional, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, customizationController, authController);
161
+ }
162
+ return sidePanelProps;
163
+ });
164
+ sideDialogsController.setSidePanels(updatedSidePanels);
165
+ }, [smallLayout]);
57
166
 
58
167
  const close = useCallback(() => {
59
168
  sideDialogsController.close();
@@ -73,12 +182,19 @@ export const useBuildSideEntityController = (navigation: NavigationController,
73
182
  }
74
183
  );
75
184
 
76
- sideDialogsController.open(propsToSidePanel({
77
- selectedSubPath: defaultSelectedView,
78
- ...props,
79
- }, navigation, smallLayout));
185
+ sideDialogsController.open(
186
+ propsToSidePanel({
187
+ selectedTab: defaultSelectedView,
188
+ ...props
189
+ },
190
+ navigation.buildUrlCollectionPath,
191
+ navigation.resolveIdsFrom,
192
+ smallLayout,
193
+ customizationController,
194
+ authController
195
+ ));
80
196
 
81
- }, [sideDialogsController, navigation, smallLayout]);
197
+ }, [sideDialogsController, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, authController.user]);
82
198
 
83
199
  const replace = useCallback((props: EntitySidePanelProps<any>) => {
84
200
 
@@ -86,9 +202,9 @@ export const useBuildSideEntityController = (navigation: NavigationController,
86
202
  throw Error("If you want to copy an entity you need to provide an entityId");
87
203
  }
88
204
 
89
- sideDialogsController.replace(propsToSidePanel(props, navigation, smallLayout));
205
+ sideDialogsController.replace(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, customizationController, authController));
90
206
 
91
- }, [navigation, sideDialogsController, smallLayout]);
207
+ }, [navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, sideDialogsController, smallLayout, authController.user]);
92
208
 
93
209
  return {
94
210
  close,
@@ -99,77 +215,88 @@ export const useBuildSideEntityController = (navigation: NavigationController,
99
215
 
100
216
  export function buildSidePanelsFromUrl(path: string, collections: EntityCollection[], newFlag: boolean): EntitySidePanelProps<any>[] {
101
217
 
102
- const navigationViewsForPath: NavigationViewInternal<any>[] = getNavigationEntriesFromPathInternal({
218
+
219
+ const navigationViewsForPath: NavigationViewInternal<any>[] = getNavigationEntriesFromPath({
103
220
  path,
104
221
  collections
105
222
  });
106
223
 
107
- const sidePanels: EntitySidePanelProps<any>[] = [];
224
+ let sidePanel: EntitySidePanelProps<any> | undefined = undefined;
108
225
  let lastCollectionPath = "";
226
+ let lastCollectionId: string | undefined = undefined;
109
227
  for (let i = 0; i < navigationViewsForPath.length; i++) {
110
228
  const navigationEntry = navigationViewsForPath[i];
111
229
 
112
230
  if (navigationEntry.type === "collection") {
113
231
  lastCollectionPath = navigationEntry.path;
232
+ lastCollectionId = navigationEntry.collection.id;
114
233
  }
115
234
 
116
- if (i > 0) { // the first collection is handled by the main navigation
117
- const previousEntry = navigationViewsForPath[i - 1];
118
- if (navigationEntry.type === "entity") {
119
- sidePanels.push({
120
- path: navigationEntry.path,
121
- entityId: navigationEntry.entityId,
122
- copy: false
123
- }
124
- );
125
- } else if (navigationEntry.type === "custom_view") {
126
- if (previousEntry.type === "entity") {
127
- const lastSidePanel: EntitySidePanelProps<any> = sidePanels[sidePanels.length - 1];
128
- if (lastSidePanel)
129
- lastSidePanel.selectedSubPath = navigationEntry.view.key;
130
- }
131
- } else if (navigationEntry.type === "collection") {
132
- if (previousEntry.type === "entity") {
133
- const lastSidePanel: EntitySidePanelProps<any> = sidePanels[sidePanels.length - 1];
134
- if (lastSidePanel)
135
- lastSidePanel.selectedSubPath = navigationEntry.collection.id ?? navigationEntry.collection.path;
136
- }
235
+ const previousEntry = navigationViewsForPath[i - 1];
236
+ if (navigationEntry.type === "entity") {
237
+ sidePanel = {
238
+ path: navigationEntry.path,
239
+ fullIdPath: navigationEntry.fullIdPath,
240
+ entityId: navigationEntry.entityId,
241
+ copy: false,
242
+ width: navigationEntry.parentCollection?.sideDialogWidth
243
+ };
244
+ } else if (navigationEntry.type === "custom_view") {
245
+ if (previousEntry?.type === "entity") {
246
+ if (sidePanel)
247
+ sidePanel.selectedTab = navigationEntry.view.key;
248
+ }
249
+ } else if (navigationEntry.type === "collection") {
250
+ if (previousEntry?.type === "entity") {
251
+ if (sidePanel)
252
+ sidePanel.selectedTab = navigationEntry.collection.id ?? navigationEntry.collection.path;
137
253
  }
138
254
  }
139
255
 
140
256
  }
141
257
 
142
258
  if (newFlag) {
143
- sidePanels.push({
259
+ sidePanel = {
144
260
  path: lastCollectionPath,
261
+ fullIdPath: lastCollectionId,
145
262
  copy: false
146
- });
263
+ }
147
264
  }
148
265
 
149
- return sidePanels;
266
+ return sidePanel ? [sidePanel] : [];
150
267
  }
151
268
 
152
- const propsToSidePanel = (props: EntitySidePanelProps<any>, navigation: NavigationController, smallLayout: boolean): SideDialogPanelProps => {
269
+ const propsToSidePanel = (props: EntitySidePanelProps,
270
+ buildUrlCollectionPath: (path: string) => string,
271
+ resolveIdsFrom: (pathWithAliases: string) => string,
272
+ smallLayout: boolean,
273
+ customizationController: CustomizationController,
274
+ authController: AuthController
275
+ ): SideDialogPanelProps => {
153
276
 
154
- const collectionPath = removeInitialAndTrailingSlashes(props.path);
277
+ const collectionPath = removeInitialAndTrailingSlashes(props.path);
155
278
 
156
- const newPath = props.entityId
157
- ? navigation.buildUrlCollectionPath(`${collectionPath}/${props.entityId}/${props.selectedSubPath || ""}`)
158
- : navigation.buildUrlCollectionPath(`${collectionPath}#${NEW_URL_HASH}`);
159
- const resolvedPath = navigation.resolveAliasesFrom(props.path);
279
+ const urlPath = props.entityId
280
+ ? buildUrlCollectionPath(`${collectionPath}/${props.entityId}${props.selectedTab ? "/" + props.selectedTab : ""}#${SIDE_URL_HASH}`)
281
+ : buildUrlCollectionPath(`${collectionPath}#${NEW_URL_HASH}`);
160
282
 
161
- const resolvedPanelProps: EntitySidePanelProps<any> = {
162
- ...props,
163
- path: resolvedPath
164
- };
283
+ const resolvedPanelProps: EntitySidePanelProps<any> = {
284
+ ...props,
285
+ formProps: props.formProps
286
+ };
165
287
 
166
- return ({
167
- key: `${props.path}/${props.entityId}`,
168
- component: <EntitySidePanel {...resolvedPanelProps}/>,
169
- urlPath: newPath,
170
- parentUrlPath: navigation.buildUrlCollectionPath(collectionPath),
171
- width: getEntityViewWidth(props, smallLayout),
172
- onClose: props.onClose
173
- });
174
- }
175
- ;
288
+ const entityViewWidth = getEntityViewWidth(props, smallLayout, customizationController, authController);
289
+ return {
290
+ key: `${props.path}/${props.entityId}`,
291
+ component: <EntitySidePanel {...resolvedPanelProps}/>,
292
+ urlPath: urlPath,
293
+ parentUrlPath: buildUrlCollectionPath(collectionPath),
294
+ width: entityViewWidth,
295
+ onClose: props.onClose,
296
+ additional: props
297
+ };
298
+ }
299
+
300
+ function isSideUrl(url: string): boolean {
301
+ return url.endsWith("#" + SIDE_URL_HASH) || url.endsWith("#" + NEW_URL_HASH);
302
+ }
@@ -1,106 +1,142 @@
1
- import React, { useCallback } from "react";
2
- import { Blocker, Transition } from "history";
3
- import { UNSAFE_NavigationContext, useNavigate } from "react-router-dom";
4
- import { Button, Dialog, DialogActions, DialogContent, Typography } from "@firecms/ui";
1
+ import React, { useCallback, useEffect, useState } from "react";
2
+ import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography } from "@firecms/ui";
5
3
 
6
- export function useNavigationUnsavedChangesDialog(when: boolean, onSuccess: () => void):
7
- {
8
- navigationWasBlocked: boolean,
9
- handleCancel: () => void,
10
- handleOk: () => void
11
- } {
12
-
13
- const [nextLocation, setNextLocation] = React.useState<any | undefined>();
14
- const { navigator } = React.useContext(UNSAFE_NavigationContext);
4
+ /**
5
+ * Type representing a pending navigation action.
6
+ */
7
+ type PendingNavigation =
8
+ | {
9
+ type: "popstate";
10
+ delta: number;
11
+ }
12
+ | {
13
+ type: "link";
14
+ href: string;
15
+ }
16
+ | null;
15
17
 
16
- const navigate = useNavigate();
18
+ /**
19
+ * Custom hook to handle navigation blocking when there are unsaved changes.
20
+ *
21
+ * @param when - Indicates whether to block navigation.
22
+ * @param onSuccess - Callback invoked when navigation is confirmed.
23
+ * @returns An object containing the state of navigation blocking and handlers.
24
+ */
25
+ export function useNavigationUnsavedChangesDialog(
26
+ when: boolean,
27
+ onSuccess: () => void
28
+ ): {
29
+ navigationWasBlocked: boolean;
30
+ handleCancel: () => void;
31
+ handleOk: () => void;
32
+ } {
33
+ const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false);
34
+ const [pendingNavigation, setPendingNavigation] = useState<PendingNavigation>(null);
17
35
 
18
- const handleCancel = () => {
19
- setNextLocation(undefined);
20
- };
36
+ /**
37
+ * Handler to cancel the navigation attempt.
38
+ */
39
+ const handleCancel = useCallback(() => {
40
+ setIsDialogOpen(false);
41
+ setPendingNavigation(null);
42
+ }, []);
21
43
 
22
- const handleOk = () => {
23
- onSuccess();
24
- setNextLocation(undefined);
25
- navigate(-1);
26
- };
44
+ /**
45
+ * Handler to confirm and proceed with the navigation.
46
+ */
47
+ const handleOk = useCallback(() => {
48
+ setIsDialogOpen(false);
49
+ if (pendingNavigation) {
50
+ onSuccess();
51
+ if (pendingNavigation.type === "popstate") {
52
+ window.history.go(pendingNavigation.delta);
53
+ } else if (pendingNavigation.type === "link") {
54
+ window.location.href = pendingNavigation.href;
55
+ }
56
+ setPendingNavigation(null);
57
+ }
58
+ }, [onSuccess, pendingNavigation]);
27
59
 
28
- const blocker: Blocker = useCallback(({
29
- action,
30
- location: nextLocation,
31
- retry
32
- }) => {
33
- switch (action) {
34
- case "REPLACE": {
35
- retry();
36
- return;
60
+ /**
61
+ * Event handler for beforeunload to handle page refresh or tab close.
62
+ */
63
+ const handleBeforeUnload = useCallback(
64
+ (e: BeforeUnloadEvent) => {
65
+ if (when) {
66
+ e.preventDefault();
67
+ e.returnValue = "";
37
68
  }
38
- case "POP": {
39
- setNextLocation(nextLocation);
69
+ },
70
+ [when]
71
+ );
72
+
73
+ /**
74
+ * Event handler for popstate to handle back and forward browser buttons.
75
+ */
76
+ const handlePopState = useCallback(
77
+ (e: PopStateEvent) => {
78
+ if (when) {
79
+ e.preventDefault();
80
+ // Assuming backward navigation; adjust delta as needed
81
+ setPendingNavigation({
82
+ type: "popstate",
83
+ delta: -1
84
+ });
85
+ setIsDialogOpen(true);
40
86
  }
41
- }
42
- }, []);
87
+ },
88
+ [when]
89
+ );
90
+
91
+ /**
92
+ * Event handler to intercept link clicks within the application.
93
+ */
94
+ const handleLinkClick = useCallback(
95
+ (e: MouseEvent) => {
96
+ if (!when) return;
43
97
 
44
- React.useEffect(() => {
45
- if (!when) return;
46
- if (nextLocation) return;
47
- if (!("block" in navigator)) return;
48
- const unblock = (navigator as any).block((tx: Transition) => {
49
- const autoUnblockingTx = {
50
- ...tx,
51
- retry() {
52
- unblock();
53
- tx.retry();
98
+ const target = e.target as HTMLElement;
99
+ const anchor = target.closest("a[href]") as HTMLAnchorElement | null;
100
+ if (anchor && anchor.host === window.location.host) {
101
+ e.preventDefault();
102
+ const href = anchor.getAttribute("href");
103
+ if (href) {
104
+ setPendingNavigation({
105
+ type: "link",
106
+ href
107
+ });
108
+ setIsDialogOpen(true);
54
109
  }
55
- };
56
- blocker(autoUnblockingTx);
57
- });
110
+ }
111
+ },
112
+ [when]
113
+ );
114
+
115
+ /**
116
+ * Effect hook to add and clean up event listeners based on the `when` condition.
117
+ */
118
+ useEffect(() => {
119
+ if (when) {
120
+ window.addEventListener("beforeunload", handleBeforeUnload);
121
+ window.addEventListener("popstate", handlePopState);
122
+ document.addEventListener("click", handleLinkClick);
123
+ } else {
124
+ window.removeEventListener("beforeunload", handleBeforeUnload);
125
+ window.removeEventListener("popstate", handlePopState);
126
+ document.removeEventListener("click", handleLinkClick);
127
+ }
58
128
 
59
- return unblock;
60
- }, [navigator, blocker, when, nextLocation]);
129
+ // Cleanup on unmount or when `when` changes
130
+ return () => {
131
+ window.removeEventListener("beforeunload", handleBeforeUnload);
132
+ window.removeEventListener("popstate", handlePopState);
133
+ document.removeEventListener("click", handleLinkClick);
134
+ };
135
+ }, [when, handleBeforeUnload, handlePopState, handleLinkClick]);
61
136
 
62
137
  return {
63
- navigationWasBlocked: Boolean(nextLocation),
138
+ navigationWasBlocked: isDialogOpen,
64
139
  handleCancel,
65
- handleOk
140
+ handleOk,
66
141
  };
67
142
  }
68
-
69
- export interface UnsavedChangesDialogProps {
70
- open: boolean;
71
- body?: React.ReactNode;
72
- title?: string;
73
- handleOk: () => void;
74
- handleCancel: () => void;
75
- }
76
-
77
- export function UnsavedChangesDialog({
78
- open,
79
- handleOk,
80
- handleCancel,
81
- body,
82
- title
83
- }: UnsavedChangesDialogProps) {
84
-
85
- return (
86
- <Dialog
87
- open={open}
88
- onOpenChange={(open) => open ? handleCancel() : handleOk()}
89
- >
90
- <DialogContent>
91
- <Typography variant={"h6"}>{title}</Typography>
92
-
93
- {body}
94
-
95
- <Typography>
96
- Are you sure you want to leave this page?
97
- </Typography>
98
-
99
- </DialogContent>
100
- <DialogActions>
101
- <Button variant="text" onClick={handleCancel} autoFocus> Cancel </Button>
102
- <Button onClick={handleOk}> Ok </Button>
103
- </DialogActions>
104
- </Dialog>
105
- );
106
- }