@firecms/core 3.0.0-canary.21 → 3.0.0-canary.211

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 (382) 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 +23 -6
  8. package/dist/components/CircularProgressCenter.d.ts +1 -1
  9. package/dist/components/ClearFilterSortButton.d.ts +5 -0
  10. package/dist/components/{DeleteConfirmationDialog.d.ts → ConfirmationDialog.d.ts} +1 -1
  11. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +13 -13
  12. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  13. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +22 -6
  14. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
  15. package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
  16. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +3 -1
  17. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  18. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
  19. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
  20. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +7 -4
  21. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +16 -2
  22. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
  23. package/dist/components/EntityCollectionView/utils.d.ts +3 -0
  24. package/dist/components/EntityJsonPreview.d.ts +3 -0
  25. package/dist/components/EntityPreview.d.ts +5 -4
  26. package/dist/components/ErrorView.d.ts +1 -1
  27. package/dist/components/HomePage/DefaultHomePage.d.ts +1 -1
  28. package/dist/components/HomePage/NavigationCardBinding.d.ts +1 -1
  29. package/dist/components/PropertyConfigBadge.d.ts +2 -1
  30. package/dist/components/PropertyIdCopyTooltip.d.ts +8 -0
  31. package/dist/components/ReferenceWidget.d.ts +3 -1
  32. package/dist/components/SelectableTable/SelectableTable.d.ts +13 -3
  33. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
  34. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  35. package/dist/components/VirtualTable/VirtualTableProps.d.ts +28 -12
  36. package/dist/components/VirtualTable/types.d.ts +3 -3
  37. package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -3
  38. package/dist/components/common/index.d.ts +2 -1
  39. package/dist/components/common/table_height.d.ts +5 -0
  40. package/dist/components/common/types.d.ts +4 -6
  41. package/dist/components/common/useColumnsIds.d.ts +3 -1
  42. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +13 -2
  43. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  44. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  45. package/dist/components/index.d.ts +4 -2
  46. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  47. package/dist/core/DefaultAppBar.d.ts +29 -0
  48. package/dist/core/DefaultDrawer.d.ts +19 -0
  49. package/dist/core/DrawerNavigationItem.d.ts +10 -0
  50. package/dist/core/EntityEditView.d.ts +35 -11
  51. package/dist/core/EntityEditViewFormActions.d.ts +2 -0
  52. package/dist/core/FireCMS.d.ts +3 -3
  53. package/dist/core/FireCMSRouter.d.ts +4 -0
  54. package/dist/core/NavigationRoutes.d.ts +2 -3
  55. package/dist/core/SideDialogs.d.ts +4 -2
  56. package/dist/core/index.d.ts +4 -4
  57. package/dist/form/EntityForm.d.ts +37 -69
  58. package/dist/form/EntityFormActions.d.ts +17 -0
  59. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  60. package/dist/form/components/ErrorFocus.d.ts +1 -1
  61. package/dist/form/components/FieldHelperText.d.ts +3 -3
  62. package/dist/form/components/FormEntry.d.ts +6 -0
  63. package/dist/form/components/FormLayout.d.ts +5 -0
  64. package/dist/form/components/LabelWithIcon.d.ts +1 -1
  65. package/dist/form/components/LabelWithIconAndTooltip.d.ts +15 -0
  66. package/dist/form/components/StorageItemPreview.d.ts +4 -4
  67. package/dist/form/components/index.d.ts +3 -1
  68. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  69. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  70. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  71. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  72. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  73. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +11 -0
  74. package/dist/form/field_bindings/{MultiSelectBinding.d.ts → MultiSelectFieldBinding.d.ts} +1 -1
  75. package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
  76. package/dist/form/field_bindings/ReferenceFieldBinding.d.ts +2 -2
  77. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  78. package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
  79. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -5
  80. package/dist/form/field_bindings/SwitchFieldBinding.d.ts +1 -2
  81. package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
  82. package/dist/form/index.d.ts +16 -18
  83. package/dist/form/useClearRestoreValue.d.ts +2 -2
  84. package/dist/hooks/data/delete.d.ts +4 -4
  85. package/dist/hooks/data/save.d.ts +4 -5
  86. package/dist/hooks/data/useCollectionFetch.d.ts +1 -1
  87. package/dist/hooks/data/useEntityFetch.d.ts +4 -3
  88. package/dist/hooks/index.d.ts +1 -0
  89. package/dist/hooks/useAuthController.d.ts +1 -1
  90. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  91. package/dist/hooks/useBuildNavigationController.d.ts +7 -5
  92. package/dist/hooks/useFireCMSContext.d.ts +1 -1
  93. package/dist/hooks/useModeController.d.ts +1 -2
  94. package/dist/hooks/useProjectLog.d.ts +8 -2
  95. package/dist/hooks/useResolvedNavigationFrom.d.ts +3 -3
  96. package/dist/hooks/useValidateAuthenticator.d.ts +4 -8
  97. package/dist/index.d.ts +1 -0
  98. package/dist/index.es.js +21921 -14330
  99. package/dist/index.es.js.map +1 -1
  100. package/dist/index.umd.js +24271 -588
  101. package/dist/index.umd.js.map +1 -1
  102. package/dist/internal/useBuildDataSource.d.ts +1 -16
  103. package/dist/internal/useBuildSideEntityController.d.ts +2 -2
  104. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  105. package/dist/preview/PropertyPreviewProps.d.ts +6 -1
  106. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  107. package/dist/preview/components/ReferencePreview.d.ts +3 -2
  108. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  109. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  110. package/dist/preview/util.d.ts +3 -3
  111. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  112. package/dist/routes/FireCMSRoute.d.ts +1 -0
  113. package/dist/routes/HomePageRoute.d.ts +3 -0
  114. package/dist/types/auth.d.ts +8 -10
  115. package/dist/types/collections.d.ts +77 -22
  116. package/dist/types/datasource.d.ts +52 -36
  117. package/dist/types/dialogs_controller.d.ts +7 -3
  118. package/dist/types/entities.d.ts +7 -2
  119. package/dist/types/entity_actions.d.ts +28 -4
  120. package/dist/types/entity_callbacks.d.ts +16 -16
  121. package/dist/types/entity_overrides.d.ts +2 -2
  122. package/dist/types/export_import.d.ts +4 -4
  123. package/dist/types/fields.d.ts +71 -42
  124. package/dist/types/firecms.d.ts +6 -2
  125. package/dist/types/firecms_context.d.ts +1 -1
  126. package/dist/types/index.d.ts +0 -1
  127. package/dist/types/navigation.d.ts +32 -14
  128. package/dist/types/permissions.d.ts +4 -4
  129. package/dist/types/plugins.d.ts +23 -12
  130. package/dist/types/properties.d.ts +52 -24
  131. package/dist/types/property_config.d.ts +0 -2
  132. package/dist/types/side_dialogs_controller.d.ts +10 -0
  133. package/dist/types/side_entity_controller.d.ts +6 -1
  134. package/dist/types/storage.d.ts +86 -3
  135. package/dist/types/user.d.ts +1 -0
  136. package/dist/util/builders.d.ts +3 -3
  137. package/dist/util/entities.d.ts +3 -3
  138. package/dist/util/entity_actions.d.ts +2 -0
  139. package/dist/util/entity_cache.d.ts +23 -0
  140. package/dist/util/icon_list.d.ts +5 -1
  141. package/dist/util/icon_synonyms.d.ts +1 -98
  142. package/dist/util/icons.d.ts +6 -3
  143. package/dist/util/index.d.ts +2 -0
  144. package/dist/util/navigation_from_path.d.ts +6 -1
  145. package/dist/util/navigation_utils.d.ts +14 -3
  146. package/dist/util/objects.d.ts +2 -1
  147. package/dist/util/permissions.d.ts +4 -4
  148. package/dist/util/plurals.d.ts +0 -2
  149. package/dist/util/property_utils.d.ts +3 -3
  150. package/dist/util/resolutions.d.ts +36 -17
  151. package/dist/util/storage.d.ts +23 -2
  152. package/dist/util/useStorageUploadController.d.ts +3 -3
  153. package/package.json +62 -48
  154. package/src/app/AppBar.tsx +18 -0
  155. package/src/app/Drawer.tsx +24 -0
  156. package/src/app/Scaffold.tsx +253 -0
  157. package/src/app/index.ts +4 -0
  158. package/src/app/useApp.tsx +32 -0
  159. package/src/components/ArrayContainer.tsx +127 -41
  160. package/src/components/CircularProgressCenter.tsx +2 -2
  161. package/src/components/ClearFilterSortButton.tsx +41 -0
  162. package/src/components/{DeleteConfirmationDialog.tsx → ConfirmationDialog.tsx} +3 -3
  163. package/src/components/DeleteEntityDialog.tsx +10 -19
  164. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +54 -37
  165. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +38 -31
  166. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +30 -9
  167. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +47 -39
  168. package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
  169. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +30 -16
  170. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +19 -17
  171. package/src/components/EntityCollectionTable/index.tsx +1 -1
  172. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +31 -36
  173. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +36 -34
  174. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +20 -8
  175. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +81 -80
  176. package/src/components/EntityCollectionView/EntityCollectionView.tsx +207 -113
  177. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -2
  178. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
  179. package/src/components/EntityCollectionView/useSelectionController.tsx +20 -7
  180. package/src/components/EntityCollectionView/utils.ts +19 -0
  181. package/src/components/EntityJsonPreview.tsx +66 -0
  182. package/src/components/EntityPreview.tsx +62 -52
  183. package/src/components/EntityView.tsx +10 -10
  184. package/src/components/ErrorView.tsx +4 -4
  185. package/src/components/HomePage/DefaultHomePage.tsx +26 -19
  186. package/src/components/HomePage/FavouritesView.tsx +6 -11
  187. package/src/components/HomePage/NavigationCard.tsx +3 -3
  188. package/src/components/HomePage/NavigationCardBinding.tsx +5 -10
  189. package/src/components/HomePage/NavigationGroup.tsx +2 -2
  190. package/src/components/HomePage/SmallNavigationCard.tsx +5 -6
  191. package/src/components/NotFoundPage.tsx +2 -2
  192. package/src/components/PropertyConfigBadge.tsx +9 -3
  193. package/src/components/PropertyIdCopyTooltip.tsx +47 -0
  194. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +19 -13
  195. package/src/components/ReferenceWidget.tsx +21 -11
  196. package/src/components/SearchIconsView.tsx +10 -7
  197. package/src/components/SelectableTable/SelectableTable.tsx +27 -12
  198. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
  199. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +25 -8
  200. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +36 -12
  201. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +92 -23
  202. package/src/components/UnsavedChangesDialog.tsx +42 -0
  203. package/src/components/VirtualTable/VirtualTable.tsx +96 -48
  204. package/src/components/VirtualTable/VirtualTableCell.tsx +1 -9
  205. package/src/components/VirtualTable/VirtualTableHeader.tsx +10 -10
  206. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
  207. package/src/components/VirtualTable/VirtualTableProps.tsx +33 -14
  208. package/src/components/VirtualTable/VirtualTableRow.tsx +5 -6
  209. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +5 -5
  210. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +3 -2
  211. package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +2 -1
  212. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +16 -28
  213. package/src/components/VirtualTable/types.tsx +2 -3
  214. package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +33 -40
  215. package/src/components/common/index.ts +2 -1
  216. package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
  217. package/src/components/common/types.tsx +4 -6
  218. package/src/components/common/useColumnsIds.tsx +24 -3
  219. package/src/components/common/useDataSourceTableController.tsx +420 -0
  220. package/src/components/common/useDebouncedCallback.tsx +20 -0
  221. package/src/components/common/useScrollRestoration.tsx +68 -0
  222. package/src/components/common/useTableSearchHelper.ts +53 -12
  223. package/src/components/index.tsx +4 -2
  224. package/src/contexts/BreacrumbsContext.tsx +38 -0
  225. package/src/contexts/DialogsProvider.tsx +5 -4
  226. package/src/contexts/ModeController.tsx +1 -3
  227. package/src/contexts/SnackbarProvider.tsx +2 -0
  228. package/src/core/DefaultAppBar.tsx +219 -0
  229. package/src/core/DefaultDrawer.tsx +185 -0
  230. package/src/core/DrawerNavigationItem.tsx +66 -0
  231. package/src/core/EntityEditView.tsx +345 -480
  232. package/src/core/EntityEditViewFormActions.tsx +199 -0
  233. package/src/core/EntitySidePanel.tsx +86 -17
  234. package/src/core/FireCMS.tsx +56 -49
  235. package/src/core/FireCMSRouter.tsx +17 -0
  236. package/src/core/NavigationRoutes.tsx +28 -38
  237. package/src/core/SideDialogs.tsx +22 -12
  238. package/src/core/field_configs.tsx +11 -12
  239. package/src/core/index.tsx +6 -5
  240. package/src/form/EntityForm.tsx +585 -539
  241. package/src/form/EntityFormActions.tsx +169 -0
  242. package/src/form/PropertyFieldBinding.tsx +85 -44
  243. package/src/form/components/CustomIdField.tsx +6 -2
  244. package/src/form/components/FieldHelperText.tsx +4 -4
  245. package/src/form/components/FormEntry.tsx +22 -0
  246. package/src/form/components/FormLayout.tsx +16 -0
  247. package/src/form/components/LabelWithIcon.tsx +30 -19
  248. package/src/form/components/LabelWithIconAndTooltip.tsx +28 -0
  249. package/src/form/components/StorageItemPreview.tsx +22 -12
  250. package/src/form/components/StorageUploadProgress.tsx +4 -5
  251. package/src/form/components/index.tsx +3 -1
  252. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +31 -19
  253. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +47 -35
  254. package/src/form/field_bindings/BlockFieldBinding.tsx +56 -34
  255. package/src/form/field_bindings/DateTimeFieldBinding.tsx +18 -14
  256. package/src/form/field_bindings/KeyValueFieldBinding.tsx +60 -51
  257. package/src/form/field_bindings/MapFieldBinding.tsx +71 -54
  258. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +153 -0
  259. package/src/form/field_bindings/{MultiSelectBinding.tsx → MultiSelectFieldBinding.tsx} +25 -20
  260. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +11 -16
  261. package/src/form/field_bindings/ReferenceFieldBinding.tsx +42 -31
  262. package/src/form/field_bindings/RepeatFieldBinding.tsx +56 -35
  263. package/src/form/field_bindings/SelectFieldBinding.tsx +22 -14
  264. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +42 -50
  265. package/src/form/field_bindings/SwitchFieldBinding.tsx +29 -24
  266. package/src/form/field_bindings/TextFieldBinding.tsx +28 -24
  267. package/src/form/index.tsx +16 -37
  268. package/src/form/useClearRestoreValue.tsx +2 -2
  269. package/src/form/validation.ts +13 -23
  270. package/src/hooks/data/delete.ts +6 -5
  271. package/src/hooks/data/save.ts +13 -9
  272. package/src/hooks/data/useCollectionFetch.tsx +3 -3
  273. package/src/hooks/data/useDataSource.tsx +11 -3
  274. package/src/hooks/data/useEntityFetch.tsx +10 -6
  275. package/src/hooks/index.tsx +1 -0
  276. package/src/hooks/useAuthController.tsx +1 -1
  277. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  278. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
  279. package/src/hooks/useBuildModeController.tsx +22 -29
  280. package/src/hooks/useBuildNavigationController.tsx +159 -54
  281. package/src/hooks/useFireCMSContext.tsx +5 -5
  282. package/src/hooks/useLargeLayout.tsx +0 -35
  283. package/src/hooks/useModeController.tsx +1 -2
  284. package/src/hooks/useProjectLog.tsx +32 -10
  285. package/src/hooks/useResolvedNavigationFrom.tsx +10 -12
  286. package/src/hooks/useValidateAuthenticator.tsx +17 -37
  287. package/src/index.ts +1 -0
  288. package/src/internal/useBuildDataSource.ts +72 -82
  289. package/src/internal/useBuildSideDialogsController.tsx +4 -2
  290. package/src/internal/useBuildSideEntityController.tsx +194 -73
  291. package/src/internal/useUnsavedChangesDialog.tsx +127 -91
  292. package/src/preview/PropertyPreview.tsx +17 -24
  293. package/src/preview/PropertyPreviewProps.tsx +7 -1
  294. package/src/preview/components/BooleanPreview.tsx +2 -2
  295. package/src/preview/components/EmptyValue.tsx +1 -1
  296. package/src/preview/components/EnumValuesChip.tsx +2 -2
  297. package/src/preview/components/ImagePreview.tsx +25 -36
  298. package/src/preview/components/ReferencePreview.tsx +22 -33
  299. package/src/preview/components/StorageThumbnail.tsx +5 -1
  300. package/src/preview/components/UrlComponentPreview.tsx +60 -28
  301. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +2 -3
  302. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +4 -3
  303. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +2 -3
  304. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +1 -2
  305. package/src/preview/property_previews/ArrayOneOfPreview.tsx +4 -5
  306. package/src/preview/property_previews/ArrayPropertyPreview.tsx +4 -5
  307. package/src/preview/property_previews/MapPropertyPreview.tsx +12 -11
  308. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +12 -12
  309. package/src/preview/property_previews/StringPropertyPreview.tsx +3 -3
  310. package/src/preview/util.ts +10 -10
  311. package/src/routes/CustomCMSRoute.tsx +21 -0
  312. package/src/routes/FireCMSRoute.tsx +235 -0
  313. package/src/routes/HomePageRoute.tsx +17 -0
  314. package/src/types/auth.tsx +9 -13
  315. package/src/types/collections.ts +94 -27
  316. package/src/types/customization_controller.tsx +0 -1
  317. package/src/types/datasource.ts +61 -43
  318. package/src/types/dialogs_controller.tsx +7 -3
  319. package/src/types/entities.ts +12 -2
  320. package/src/types/entity_actions.tsx +32 -7
  321. package/src/types/entity_callbacks.ts +18 -18
  322. package/src/types/entity_overrides.tsx +2 -2
  323. package/src/types/export_import.ts +4 -4
  324. package/src/types/fields.tsx +81 -46
  325. package/src/types/firecms.tsx +7 -2
  326. package/src/types/firecms_context.tsx +1 -1
  327. package/src/types/index.ts +0 -1
  328. package/src/types/navigation.ts +38 -18
  329. package/src/types/permissions.ts +5 -5
  330. package/src/types/plugins.tsx +25 -14
  331. package/src/types/properties.ts +60 -25
  332. package/src/types/property_config.tsx +0 -2
  333. package/src/types/side_dialogs_controller.tsx +15 -0
  334. package/src/types/side_entity_controller.tsx +6 -1
  335. package/src/types/storage.ts +94 -3
  336. package/src/types/user.ts +2 -0
  337. package/src/util/builders.ts +10 -8
  338. package/src/util/entities.ts +9 -6
  339. package/src/util/entity_actions.ts +28 -0
  340. package/src/util/entity_cache.ts +204 -0
  341. package/src/util/enums.ts +1 -1
  342. package/src/util/icon_list.ts +16 -10
  343. package/src/util/icon_synonyms.ts +3 -100
  344. package/src/util/icons.tsx +17 -7
  345. package/src/util/index.ts +2 -0
  346. package/src/util/join_collections.ts +3 -0
  347. package/src/util/make_properties_editable.ts +13 -5
  348. package/src/util/navigation_from_path.ts +13 -6
  349. package/src/util/navigation_utils.ts +59 -7
  350. package/src/util/objects.ts +48 -28
  351. package/src/util/parent_references_from_path.ts +3 -3
  352. package/src/util/permissions.ts +9 -8
  353. package/src/util/plurals.ts +0 -2
  354. package/src/util/property_utils.tsx +11 -4
  355. package/src/util/references.ts +11 -2
  356. package/src/util/resolutions.ts +100 -46
  357. package/src/util/storage.ts +79 -21
  358. package/src/util/strings.ts +2 -2
  359. package/src/util/useStorageUploadController.tsx +71 -27
  360. package/dist/components/FireCMSAppBar.d.ts +0 -26
  361. package/dist/components/PropertyIdCopyTooltipContent.d.ts +0 -3
  362. package/dist/components/VirtualTable/common.d.ts +0 -2
  363. package/dist/core/Drawer.d.ts +0 -23
  364. package/dist/core/Scaffold.d.ts +0 -55
  365. package/dist/core/SideEntityView.d.ts +0 -7
  366. package/dist/form/components/FormikArrayContainer.d.ts +0 -18
  367. package/dist/form/field_bindings/MarkdownFieldBinding.d.ts +0 -9
  368. package/dist/internal/useBuildCustomizationController.d.ts +0 -2
  369. package/dist/internal/useLocaleConfig.d.ts +0 -1
  370. package/dist/types/appcheck.d.ts +0 -26
  371. package/src/components/FireCMSAppBar.tsx +0 -165
  372. package/src/components/PropertyIdCopyTooltipContent.tsx +0 -28
  373. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -225
  374. package/src/core/Drawer.tsx +0 -191
  375. package/src/core/Scaffold.tsx +0 -281
  376. package/src/core/SideEntityView.tsx +0 -38
  377. package/src/form/components/FormikArrayContainer.tsx +0 -44
  378. package/src/form/field_bindings/MarkdownFieldBinding.tsx +0 -695
  379. package/src/internal/useBuildCustomizationController.tsx +0 -5
  380. package/src/internal/useLocaleConfig.tsx +0 -18
  381. package/src/types/appcheck.ts +0 -29
  382. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -1,26 +1,100 @@
1
1
  import { useCallback, useEffect, useRef } from "react";
2
2
  import {
3
+ CustomizationController,
3
4
  EntityCollection,
4
5
  EntitySidePanelProps,
5
6
  NavigationController,
7
+ ResolvedProperty,
6
8
  SideDialogPanelProps,
7
9
  SideDialogsController,
8
10
  SideEntityController
9
11
  } from "../types";
10
- import { getNavigationEntriesFromPathInternal, NavigationViewInternal } from "../util/navigation_from_path";
12
+ import { getNavigationEntriesFromPath, NavigationViewInternal } from "../util/navigation_from_path";
11
13
  import { useLocation } from "react-router-dom";
12
- import { removeInitialAndTrailingSlashes, resolveDefaultSelectedView } from "../util";
14
+ import {
15
+ removeInitialAndTrailingSlashes,
16
+ resolveCollection,
17
+ resolveDefaultSelectedView,
18
+ resolvedSelectedEntityView
19
+ } from "../util";
13
20
  import { ADDITIONAL_TAB_WIDTH, CONTAINER_FULL_WIDTH, FORM_CONTAINER_WIDTH } from "./common";
14
- import { useLargeLayout } from "../hooks";
21
+ import { useCustomizationController, useLargeLayout } from "../hooks";
15
22
  import { EntitySidePanel } from "../core/EntitySidePanel";
23
+ import { JSON_TAB_VALUE } from "../core/EntityEditView";
16
24
 
17
- const NEW_URL_HASH = "new";
25
+ const NEW_URL_HASH = "new_side";
26
+ const SIDE_URL_HASH = "side";
18
27
 
19
- export function getEntityViewWidth(props: EntitySidePanelProps<any>, small: boolean): string {
28
+ export function getEntityViewWidth(props: EntitySidePanelProps<any>, small: boolean, customizationController: CustomizationController): string {
20
29
  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
30
+
31
+ const {
32
+ selectedSecondaryForm
33
+ } = resolvedSelectedEntityView(props.collection?.entityViews, customizationController, props.selectedTab);
34
+
35
+ const shouldUseSmallLayout = !props.selectedTab || props.selectedTab === JSON_TAB_VALUE || Boolean(selectedSecondaryForm);
36
+
37
+ let resolvedWidth: string | undefined;
38
+ if (props.width) {
39
+ resolvedWidth = typeof props.width === "number" ? `${props.width}px` : props.width;
40
+ } else if (props.collection?.sideDialogWidth) {
41
+ resolvedWidth = typeof props.collection.sideDialogWidth === "number" ? `${props.collection.sideDialogWidth}px` : props.collection.sideDialogWidth;
42
+ }
43
+
44
+ if (!shouldUseSmallLayout) {
45
+ return `calc(${ADDITIONAL_TAB_WIDTH} + ${resolvedWidth ?? FORM_CONTAINER_WIDTH})`
46
+ } else {
47
+ if (resolvedWidth) {
48
+ return resolvedWidth
49
+ } else if (!props.collection) {
50
+ return FORM_CONTAINER_WIDTH;
51
+ } else {
52
+ return calculateCollectionDesiredWidth(props.collection);
53
+ }
54
+ }
55
+ }
56
+
57
+ const collectionViewWidthCache: { [key: string]: string } = {};
58
+
59
+ function calculateCollectionDesiredWidth(collection: EntityCollection<any>): string {
60
+ if (collectionViewWidthCache[collection.id]) {
61
+ return collectionViewWidthCache[collection.id];
62
+ }
63
+ const resolvedCollection = resolveCollection({
64
+ collection,
65
+ path: "__ignored",
66
+ ignoreMissingFields: true
67
+ });
68
+
69
+ let result = FORM_CONTAINER_WIDTH
70
+ if (resolvedCollection?.properties) {
71
+ const values = Object.values(resolvedCollection.properties).map((p: ResolvedProperty) => getNestedPropertiesDepth(p));
72
+ const maxDepth = Math.max(...values);
73
+ if (maxDepth < 3) {
74
+ result = FORM_CONTAINER_WIDTH;
75
+ } else {
76
+ result = 768 + 32 * (maxDepth - 2) + "px";
77
+ }
78
+ }
79
+ collectionViewWidthCache[collection.id] = result;
80
+ return result;
81
+ }
82
+
83
+ function getNestedPropertiesDepth(property: ResolvedProperty, accumulator: number = 0): number {
84
+ if (property.dataType === "map" && property.properties) {
85
+ const values = Object.values(property.properties).flatMap((property) => getNestedPropertiesDepth(property, accumulator + 1));
86
+ return Math.max(...values);
87
+ } else if (property.dataType === "array" && property.oneOf) {
88
+ return accumulator + 3;
89
+ } else if (property.dataType === "array" && property.of) {
90
+ if (Array.isArray(property.of)) {
91
+ return Math.max(...property.of.map((p) => getNestedPropertiesDepth(p, accumulator + 1)));
92
+ } else {
93
+ return getNestedPropertiesDepth(property.of, accumulator + 1);
94
+ }
95
+ } else {
96
+ return accumulator + 1;
97
+ }
24
98
  }
25
99
 
26
100
  export const useBuildSideEntityController = (navigation: NavigationController,
@@ -28,30 +102,64 @@ export const useBuildSideEntityController = (navigation: NavigationController,
28
102
 
29
103
  const location = useLocation();
30
104
  const initialised = useRef<boolean>(false);
105
+ const customizationController = useCustomizationController();
31
106
 
32
107
  const smallLayout = !useLargeLayout();
33
108
 
34
109
  // only on initialisation, create panels from URL
35
110
  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}`;
111
+
112
+ const newFlag = location.hash === `#${NEW_URL_HASH}`;
113
+ const sideFlag = location.hash === `#${SIDE_URL_HASH}`;
114
+
115
+ if (!navigation.loading) {
116
+ if ((newFlag || sideFlag) && navigation.isUrlCollectionPath(location.pathname)) {
40
117
  const entityOrCollectionPath = navigation.urlPathToDataPath(location.pathname);
41
118
  const panelsFromUrl = buildSidePanelsFromUrl(entityOrCollectionPath, navigation.collections ?? [], newFlag);
42
119
  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);
120
+ const props = panelsFromUrl[i];
121
+ if (i === 0)
122
+ sideDialogsController.replace(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, customizationController));
123
+ else
124
+ sideDialogsController.open(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, customizationController))
50
125
  }
51
126
  }
52
127
  initialised.current = true;
53
128
  }
54
- }, [location, navigation, sideDialogsController, smallLayout]);
129
+ }, [navigation.loading]);
130
+
131
+ // sync panels if URL changes with #side
132
+ const currentPanelKeys = sideDialogsController.sidePanels.map(p => p.key);
133
+ useEffect(() => {
134
+ if (initialised.current) {
135
+ const sideFlag = location.hash === `#${SIDE_URL_HASH}`;
136
+ if (sideFlag) {
137
+ const entityOrCollectionPath = navigation.urlPathToDataPath(location.pathname);
138
+ const panelsFromUrl = buildSidePanelsFromUrl(entityOrCollectionPath, navigation.collections ?? [], false);
139
+ // if we have more panels than determined by the url, we ignore the url. We might have references open
140
+ if (panelsFromUrl.length <= currentPanelKeys.length) {
141
+ return;
142
+ }
143
+ const lastPanel = panelsFromUrl[panelsFromUrl.length - 1];
144
+ const panelProps = propsToSidePanel(lastPanel, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, customizationController);
145
+ const lastCurrentPanel = currentPanelKeys.length > 0 ? currentPanelKeys[currentPanelKeys.length - 1] : undefined;
146
+ if (!lastCurrentPanel || lastCurrentPanel !== panelProps.key) {
147
+ sideDialogsController.replace(panelProps);
148
+ }
149
+ }
150
+ }
151
+ }, [location.pathname, location.hash, currentPanelKeys]);
152
+
153
+ // update side panels to match browser size
154
+ useEffect(() => {
155
+ const updatedSidePanels = sideDialogsController.sidePanels.map(sidePanelProps => {
156
+ if (sidePanelProps.additional) {
157
+ return propsToSidePanel(sidePanelProps.additional, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, customizationController);
158
+ }
159
+ return sidePanelProps;
160
+ });
161
+ sideDialogsController.setSidePanels(updatedSidePanels);
162
+ }, [smallLayout]);
55
163
 
56
164
  const close = useCallback(() => {
57
165
  sideDialogsController.close();
@@ -71,12 +179,18 @@ export const useBuildSideEntityController = (navigation: NavigationController,
71
179
  }
72
180
  );
73
181
 
74
- sideDialogsController.open(propsToSidePanel({
75
- selectedSubPath: defaultSelectedView,
76
- ...props,
77
- }, navigation, smallLayout));
182
+ sideDialogsController.open(
183
+ propsToSidePanel({
184
+ selectedTab: defaultSelectedView,
185
+ ...props
186
+ },
187
+ navigation.buildUrlCollectionPath,
188
+ navigation.resolveIdsFrom,
189
+ smallLayout,
190
+ customizationController
191
+ ));
78
192
 
79
- }, [sideDialogsController, navigation, smallLayout]);
193
+ }, [sideDialogsController, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout]);
80
194
 
81
195
  const replace = useCallback((props: EntitySidePanelProps<any>) => {
82
196
 
@@ -84,9 +198,9 @@ export const useBuildSideEntityController = (navigation: NavigationController,
84
198
  throw Error("If you want to copy an entity you need to provide an entityId");
85
199
  }
86
200
 
87
- sideDialogsController.replace(propsToSidePanel(props, navigation, smallLayout));
201
+ sideDialogsController.replace(propsToSidePanel(props, navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, smallLayout, customizationController));
88
202
 
89
- }, [navigation, sideDialogsController, smallLayout]);
203
+ }, [navigation.buildUrlCollectionPath, navigation.resolveIdsFrom, sideDialogsController, smallLayout]);
90
204
 
91
205
  return {
92
206
  close,
@@ -97,12 +211,12 @@ export const useBuildSideEntityController = (navigation: NavigationController,
97
211
 
98
212
  export function buildSidePanelsFromUrl(path: string, collections: EntityCollection[], newFlag: boolean): EntitySidePanelProps<any>[] {
99
213
 
100
- const navigationViewsForPath: NavigationViewInternal<any>[] = getNavigationEntriesFromPathInternal({
214
+ const navigationViewsForPath: NavigationViewInternal<any>[] = getNavigationEntriesFromPath({
101
215
  path,
102
216
  collections
103
217
  });
104
218
 
105
- const sidePanels: EntitySidePanelProps<any>[] = [];
219
+ let sidePanel: EntitySidePanelProps<any> | undefined = undefined;
106
220
  let lastCollectionPath = "";
107
221
  for (let i = 0; i < navigationViewsForPath.length; i++) {
108
222
  const navigationEntry = navigationViewsForPath[i];
@@ -111,63 +225,70 @@ export function buildSidePanelsFromUrl(path: string, collections: EntityCollecti
111
225
  lastCollectionPath = navigationEntry.path;
112
226
  }
113
227
 
114
- if (i > 0) { // the first collection is handled by the main navigation
115
- const previousEntry = navigationViewsForPath[i - 1];
116
- if (navigationEntry.type === "entity") {
117
- sidePanels.push({
118
- path: navigationEntry.path,
119
- entityId: navigationEntry.entityId,
120
- copy: false
121
- }
122
- );
123
- } else if (navigationEntry.type === "custom_view") {
124
- if (previousEntry.type === "entity") {
125
- const lastSidePanel: EntitySidePanelProps<any> = sidePanels[sidePanels.length - 1];
126
- if (lastSidePanel)
127
- lastSidePanel.selectedSubPath = navigationEntry.view.key;
128
- }
129
- } else if (navigationEntry.type === "collection") {
130
- if (previousEntry.type === "entity") {
131
- const lastSidePanel: EntitySidePanelProps<any> = sidePanels[sidePanels.length - 1];
132
- if (lastSidePanel)
133
- lastSidePanel.selectedSubPath = navigationEntry.collection.id ?? navigationEntry.collection.path;
134
- }
228
+ const previousEntry = navigationViewsForPath[i - 1];
229
+ if (navigationEntry.type === "entity") {
230
+ sidePanel = {
231
+ path: navigationEntry.path,
232
+ entityId: navigationEntry.entityId,
233
+ copy: false,
234
+ width: navigationEntry.parentCollection?.sideDialogWidth
235
+ };
236
+ } else if (navigationEntry.type === "custom_view") {
237
+ if (previousEntry?.type === "entity") {
238
+ if (sidePanel)
239
+ sidePanel.selectedTab = navigationEntry.view.key;
240
+ }
241
+ } else if (navigationEntry.type === "collection") {
242
+ if (previousEntry?.type === "entity") {
243
+ if (sidePanel)
244
+ sidePanel.selectedTab = navigationEntry.collection.id ?? navigationEntry.collection.path;
135
245
  }
136
246
  }
137
247
 
138
248
  }
139
249
 
140
250
  if (newFlag) {
141
- sidePanels.push({
251
+ sidePanel = {
142
252
  path: lastCollectionPath,
143
253
  copy: false
144
- });
254
+ }
145
255
  }
146
256
 
147
- return sidePanels;
257
+ return sidePanel ? [sidePanel] : [];
148
258
  }
149
259
 
150
- const propsToSidePanel = (props: EntitySidePanelProps<any>, navigation: NavigationController, smallLayout: boolean): SideDialogPanelProps => {
260
+ const propsToSidePanel = (props: EntitySidePanelProps,
261
+ buildUrlCollectionPath: (path: string) => string,
262
+ resolveIdsFrom: (pathWithAliases: string) => string,
263
+ smallLayout: boolean,
264
+ customizationController: CustomizationController): SideDialogPanelProps => {
265
+
266
+ const collectionPath = removeInitialAndTrailingSlashes(props.path);
151
267
 
152
- const collectionPath = removeInitialAndTrailingSlashes(props.path);
268
+ const newPath = props.entityId
269
+ ? buildUrlCollectionPath(`${collectionPath}/${props.entityId}${props.selectedTab ? "/" + props.selectedTab : ""}#${SIDE_URL_HASH}`)
270
+ : buildUrlCollectionPath(`${collectionPath}#${NEW_URL_HASH}`);
271
+ const resolvedPath = resolveIdsFrom(props.path);
153
272
 
154
- const newPath = props.entityId
155
- ? navigation.buildUrlCollectionPath(`${collectionPath}/${props.entityId}/${props.selectedSubPath || ""}`)
156
- : navigation.buildUrlCollectionPath(`${collectionPath}#${NEW_URL_HASH}`);
157
- const resolvedPath = navigation.resolveAliasesFrom(props.path);
273
+ const resolvedPanelProps: EntitySidePanelProps<any> = {
274
+ ...props,
275
+ path: resolvedPath,
276
+ formProps: props.formProps
277
+ };
158
278
 
159
- const resolvedPanelProps: EntitySidePanelProps<any> = {
160
- ...props,
161
- path: resolvedPath
162
- };
279
+ const entityViewWidth = getEntityViewWidth(props, smallLayout, customizationController);
163
280
 
164
- return ({
165
- key: `${props.path}/${props.entityId}`,
166
- component: <EntitySidePanel {...resolvedPanelProps}/>,
167
- urlPath: newPath,
168
- parentUrlPath: navigation.buildUrlCollectionPath(collectionPath),
169
- width: getEntityViewWidth(props, smallLayout),
170
- onClose: props.onClose
171
- });
172
- }
173
- ;
281
+ return {
282
+ key: `${props.path}/${props.entityId}`,
283
+ component: <EntitySidePanel {...resolvedPanelProps}/>,
284
+ urlPath: newPath,
285
+ parentUrlPath: buildUrlCollectionPath(collectionPath),
286
+ width: entityViewWidth,
287
+ onClose: props.onClose,
288
+ additional: props
289
+ };
290
+ }
291
+
292
+ function isSideUrl(url: string): boolean {
293
+ return url.endsWith("#" + SIDE_URL_HASH) || url.endsWith("#" + NEW_URL_HASH);
294
+ }
@@ -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
- }