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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. package/README.md +2 -2
  2. package/dist/app/Drawer.d.ts +0 -1
  3. package/dist/app/Scaffold.d.ts +4 -0
  4. package/dist/components/ArrayContainer.d.ts +31 -12
  5. package/dist/components/{DeleteConfirmationDialog.d.ts → ConfirmationDialog.d.ts} +1 -1
  6. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +3 -1
  7. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  8. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +17 -3
  9. package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +1 -1
  10. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  11. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +6 -3
  12. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +8 -0
  13. package/dist/components/EntityCollectionView/utils.d.ts +3 -0
  14. package/dist/components/EntityJsonPreview.d.ts +3 -0
  15. package/dist/components/EntityPreview.d.ts +8 -6
  16. package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
  17. package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
  18. package/dist/components/HomePage/NavigationCard.d.ts +3 -1
  19. package/dist/components/HomePage/NavigationCardBinding.d.ts +3 -2
  20. package/dist/components/HomePage/NavigationGroup.d.ts +8 -1
  21. package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
  22. package/dist/components/PropertyConfigBadge.d.ts +2 -1
  23. package/dist/components/PropertyIdCopyTooltip.d.ts +8 -0
  24. package/dist/components/SelectableTable/SelectableTable.d.ts +13 -3
  25. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +1 -1
  26. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  27. package/dist/components/VirtualTable/VirtualTableProps.d.ts +11 -2
  28. package/dist/components/common/default_entity_actions.d.ts +0 -2
  29. package/dist/components/common/index.d.ts +1 -1
  30. package/dist/components/common/useColumnsIds.d.ts +1 -0
  31. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +10 -2
  32. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  33. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  34. package/dist/components/index.d.ts +3 -1
  35. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  36. package/dist/core/DefaultAppBar.d.ts +8 -2
  37. package/dist/core/DrawerNavigationItem.d.ts +2 -1
  38. package/dist/core/EntityEditView.d.ts +40 -22
  39. package/dist/core/EntityEditViewFormActions.d.ts +2 -0
  40. package/dist/core/FireCMS.d.ts +2 -2
  41. package/dist/core/FireCMSRouter.d.ts +4 -0
  42. package/dist/core/NavigationRoutes.d.ts +0 -1
  43. package/dist/core/SideDialogs.d.ts +4 -2
  44. package/dist/core/field_configs.d.ts +1 -1
  45. package/dist/core/index.d.ts +2 -1
  46. package/dist/form/EntityForm.d.ts +50 -0
  47. package/dist/form/EntityFormActions.d.ts +21 -0
  48. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  49. package/dist/form/components/FormEntry.d.ts +6 -0
  50. package/dist/form/components/FormLayout.d.ts +5 -0
  51. package/dist/form/components/LabelWithIcon.d.ts +1 -1
  52. package/dist/form/components/LabelWithIconAndTooltip.d.ts +15 -0
  53. package/dist/form/components/index.d.ts +3 -1
  54. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  55. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  56. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  57. package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
  58. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  59. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +11 -0
  60. package/dist/form/field_bindings/{MultiSelectBinding.d.ts → MultiSelectFieldBinding.d.ts} +1 -1
  61. package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
  62. package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
  63. package/dist/form/field_bindings/ReferenceFieldBinding.d.ts +2 -2
  64. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  65. package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
  66. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -10
  67. package/dist/form/field_bindings/SwitchFieldBinding.d.ts +1 -2
  68. package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
  69. package/dist/form/index.d.ts +17 -16
  70. package/dist/form/useClearRestoreValue.d.ts +2 -2
  71. package/dist/hooks/data/delete.d.ts +4 -4
  72. package/dist/hooks/data/save.d.ts +3 -3
  73. package/dist/hooks/data/useCollectionFetch.d.ts +1 -1
  74. package/dist/hooks/data/useEntityFetch.d.ts +4 -3
  75. package/dist/hooks/useAuthController.d.ts +1 -1
  76. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  77. package/dist/hooks/useBuildNavigationController.d.ts +57 -12
  78. package/dist/hooks/useFireCMSContext.d.ts +1 -1
  79. package/dist/hooks/useModeController.d.ts +1 -2
  80. package/dist/hooks/useProjectLog.d.ts +7 -1
  81. package/dist/hooks/useResolvedNavigationFrom.d.ts +3 -3
  82. package/dist/hooks/useValidateAuthenticator.d.ts +3 -3
  83. package/dist/index.es.js +20108 -14471
  84. package/dist/index.es.js.map +1 -1
  85. package/dist/index.umd.js +20039 -14407
  86. package/dist/index.umd.js.map +1 -1
  87. package/dist/internal/useBuildDataSource.d.ts +3 -2
  88. package/dist/internal/useBuildSideEntityController.d.ts +3 -3
  89. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  90. package/dist/preview/PropertyPreviewProps.d.ts +1 -1
  91. package/dist/preview/components/EnumValuesChip.d.ts +1 -1
  92. package/dist/preview/components/ReferencePreview.d.ts +2 -2
  93. package/dist/preview/util.d.ts +3 -3
  94. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  95. package/dist/routes/FireCMSRoute.d.ts +1 -0
  96. package/dist/routes/HomePageRoute.d.ts +3 -0
  97. package/dist/types/analytics.d.ts +1 -1
  98. package/dist/types/auth.d.ts +7 -9
  99. package/dist/types/collections.d.ts +86 -25
  100. package/dist/types/customization_controller.d.ts +8 -0
  101. package/dist/types/datasource.d.ts +19 -17
  102. package/dist/types/dialogs_controller.d.ts +7 -3
  103. package/dist/types/entities.d.ts +2 -1
  104. package/dist/types/entity_actions.d.ts +58 -8
  105. package/dist/types/entity_callbacks.d.ts +16 -16
  106. package/dist/types/entity_overrides.d.ts +2 -2
  107. package/dist/types/export_import.d.ts +4 -4
  108. package/dist/types/fields.d.ts +43 -17
  109. package/dist/types/firecms.d.ts +16 -3
  110. package/dist/types/firecms_context.d.ts +1 -1
  111. package/dist/types/navigation.d.ts +60 -17
  112. package/dist/types/permissions.d.ts +4 -4
  113. package/dist/types/plugins.d.ts +42 -9
  114. package/dist/types/properties.d.ts +65 -22
  115. package/dist/types/property_config.d.ts +1 -3
  116. package/dist/types/roles.d.ts +3 -0
  117. package/dist/types/side_dialogs_controller.d.ts +10 -0
  118. package/dist/types/side_entity_controller.d.ts +14 -1
  119. package/dist/types/storage.d.ts +75 -0
  120. package/dist/types/user.d.ts +1 -0
  121. package/dist/util/builders.d.ts +3 -3
  122. package/dist/util/callbacks.d.ts +2 -0
  123. package/dist/util/createFormexStub.d.ts +2 -0
  124. package/dist/util/entities.d.ts +2 -2
  125. package/dist/util/entity_actions.d.ts +2 -0
  126. package/dist/util/entity_cache.d.ts +23 -0
  127. package/dist/util/icon_synonyms.d.ts +0 -1
  128. package/dist/util/icons.d.ts +5 -2
  129. package/dist/util/index.d.ts +3 -0
  130. package/dist/util/navigation_from_path.d.ts +10 -1
  131. package/dist/util/navigation_utils.d.ts +13 -1
  132. package/dist/util/objects.d.ts +2 -1
  133. package/dist/util/permissions.d.ts +4 -4
  134. package/dist/util/property_utils.d.ts +4 -4
  135. package/dist/util/references.d.ts +2 -2
  136. package/dist/util/resolutions.d.ts +30 -6
  137. package/dist/util/storage.d.ts +1 -1
  138. package/dist/util/useStorageUploadController.d.ts +2 -2
  139. package/package.json +133 -125
  140. package/src/app/Drawer.tsx +0 -1
  141. package/src/app/Scaffold.tsx +33 -29
  142. package/src/components/ArrayContainer.tsx +447 -229
  143. package/src/components/CircularProgressCenter.tsx +1 -1
  144. package/src/components/ClearFilterSortButton.tsx +1 -1
  145. package/src/components/{DeleteConfirmationDialog.tsx → ConfirmationDialog.tsx} +12 -11
  146. package/src/components/DeleteEntityDialog.tsx +13 -20
  147. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +59 -25
  148. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +23 -17
  149. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +20 -3
  150. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +35 -9
  151. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +21 -16
  152. package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +6 -12
  153. package/src/components/EntityCollectionTable/index.tsx +1 -1
  154. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +6 -6
  155. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +35 -26
  156. package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +20 -8
  157. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +132 -101
  158. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
  159. package/src/components/EntityCollectionView/EntityCollectionView.tsx +178 -85
  160. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +7 -4
  161. package/src/components/EntityCollectionView/useSelectionController.tsx +5 -4
  162. package/src/components/EntityCollectionView/utils.ts +19 -0
  163. package/src/components/EntityJsonPreview.tsx +66 -0
  164. package/src/components/EntityPreview.tsx +75 -57
  165. package/src/components/EntityView.tsx +8 -5
  166. package/src/components/ErrorView.tsx +3 -3
  167. package/src/components/FireCMSLogo.tsx +7 -51
  168. package/src/components/HomePage/DefaultHomePage.tsx +522 -160
  169. package/src/components/HomePage/FavouritesView.tsx +9 -14
  170. package/src/components/HomePage/HomePageDnD.tsx +642 -0
  171. package/src/components/HomePage/NavigationCard.tsx +47 -38
  172. package/src/components/HomePage/NavigationCardBinding.tsx +16 -15
  173. package/src/components/HomePage/NavigationGroup.tsx +144 -30
  174. package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
  175. package/src/components/HomePage/SmallNavigationCard.tsx +1 -2
  176. package/src/components/NotFoundPage.tsx +2 -2
  177. package/src/components/PropertyConfigBadge.tsx +9 -3
  178. package/src/components/PropertyIdCopyTooltip.tsx +47 -0
  179. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +22 -13
  180. package/src/components/SearchIconsView.tsx +2 -2
  181. package/src/components/SelectableTable/SelectableTable.tsx +154 -142
  182. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +4 -2
  183. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +10 -8
  184. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +59 -10
  185. package/src/components/UnsavedChangesDialog.tsx +46 -0
  186. package/src/components/VirtualTable/VirtualTable.tsx +65 -44
  187. package/src/components/VirtualTable/VirtualTableCell.tsx +0 -8
  188. package/src/components/VirtualTable/VirtualTableHeader.tsx +8 -8
  189. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +1 -1
  190. package/src/components/VirtualTable/VirtualTableProps.tsx +12 -2
  191. package/src/components/VirtualTable/VirtualTableRow.tsx +1 -1
  192. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +4 -4
  193. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +2 -2
  194. package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +2 -1
  195. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +16 -28
  196. package/src/components/common/default_entity_actions.tsx +62 -42
  197. package/src/components/common/index.ts +1 -1
  198. package/src/components/common/useColumnsIds.tsx +1 -1
  199. package/src/components/common/useDataSourceTableController.tsx +420 -0
  200. package/src/components/common/useDebouncedCallback.tsx +20 -0
  201. package/src/components/common/useScrollRestoration.tsx +68 -0
  202. package/src/components/common/useTableSearchHelper.ts +1 -0
  203. package/src/components/index.tsx +4 -1
  204. package/src/contexts/BreacrumbsContext.tsx +38 -0
  205. package/src/contexts/DialogsProvider.tsx +3 -2
  206. package/src/contexts/ModeController.tsx +1 -3
  207. package/src/contexts/SnackbarProvider.tsx +2 -0
  208. package/src/core/DefaultAppBar.tsx +124 -85
  209. package/src/core/DefaultDrawer.tsx +30 -22
  210. package/src/core/DrawerNavigationItem.tsx +32 -28
  211. package/src/core/EntityEditView.tsx +388 -995
  212. package/src/core/EntityEditViewFormActions.tsx +329 -0
  213. package/src/core/EntitySidePanel.tsx +88 -20
  214. package/src/core/FireCMS.tsx +46 -25
  215. package/src/core/FireCMSRouter.tsx +17 -0
  216. package/src/core/NavigationRoutes.tsx +23 -32
  217. package/src/core/SideDialogs.tsx +22 -12
  218. package/src/core/field_configs.tsx +24 -10
  219. package/src/core/index.tsx +4 -2
  220. package/src/form/EntityForm.tsx +814 -0
  221. package/src/form/EntityFormActions.tsx +211 -0
  222. package/src/form/PropertyFieldBinding.tsx +55 -41
  223. package/src/form/components/CustomIdField.tsx +9 -3
  224. package/src/form/components/FieldHelperText.tsx +1 -1
  225. package/src/form/components/FormEntry.tsx +22 -0
  226. package/src/form/components/FormLayout.tsx +16 -0
  227. package/src/form/components/LabelWithIcon.tsx +30 -19
  228. package/src/form/components/LabelWithIconAndTooltip.tsx +28 -0
  229. package/src/form/components/StorageItemPreview.tsx +5 -4
  230. package/src/form/components/StorageUploadProgress.tsx +2 -3
  231. package/src/form/components/index.tsx +3 -1
  232. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +30 -18
  233. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +47 -36
  234. package/src/form/field_bindings/BlockFieldBinding.tsx +55 -33
  235. package/src/form/field_bindings/DateTimeFieldBinding.tsx +18 -14
  236. package/src/form/field_bindings/KeyValueFieldBinding.tsx +19 -15
  237. package/src/form/field_bindings/MapFieldBinding.tsx +72 -62
  238. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +159 -0
  239. package/src/form/field_bindings/{MultiSelectBinding.tsx → MultiSelectFieldBinding.tsx} +26 -21
  240. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +10 -8
  241. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
  242. package/src/form/field_bindings/ReferenceFieldBinding.tsx +28 -19
  243. package/src/form/field_bindings/RepeatFieldBinding.tsx +56 -32
  244. package/src/form/field_bindings/SelectFieldBinding.tsx +22 -13
  245. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +247 -168
  246. package/src/form/field_bindings/SwitchFieldBinding.tsx +29 -24
  247. package/src/form/field_bindings/TextFieldBinding.tsx +28 -24
  248. package/src/form/index.tsx +17 -37
  249. package/src/form/useClearRestoreValue.tsx +2 -2
  250. package/src/form/validation.ts +12 -6
  251. package/src/hooks/data/delete.ts +6 -5
  252. package/src/hooks/data/save.ts +26 -35
  253. package/src/hooks/data/useCollectionFetch.tsx +3 -3
  254. package/src/hooks/data/useDataSource.tsx +10 -2
  255. package/src/hooks/data/useEntityFetch.tsx +10 -6
  256. package/src/hooks/useAuthController.tsx +1 -1
  257. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  258. package/src/hooks/useBrowserTitleAndIcon.tsx +1 -1
  259. package/src/hooks/useBuildModeController.tsx +15 -28
  260. package/src/hooks/useBuildNavigationController.tsx +386 -124
  261. package/src/hooks/useFireCMSContext.tsx +3 -33
  262. package/src/hooks/useLargeLayout.tsx +0 -35
  263. package/src/hooks/useModeController.tsx +1 -2
  264. package/src/hooks/useProjectLog.tsx +16 -5
  265. package/src/hooks/useResolvedNavigationFrom.tsx +9 -11
  266. package/src/hooks/useValidateAuthenticator.tsx +3 -3
  267. package/src/internal/useBuildDataSource.ts +67 -80
  268. package/src/internal/useBuildSideDialogsController.tsx +4 -2
  269. package/src/internal/useBuildSideEntityController.tsx +149 -86
  270. package/src/internal/useUnsavedChangesDialog.tsx +127 -91
  271. package/src/preview/PropertyPreview.tsx +28 -12
  272. package/src/preview/PropertyPreviewProps.tsx +1 -1
  273. package/src/preview/components/BooleanPreview.tsx +1 -1
  274. package/src/preview/components/EmptyValue.tsx +1 -1
  275. package/src/preview/components/EnumValuesChip.tsx +1 -1
  276. package/src/preview/components/ImagePreview.tsx +10 -9
  277. package/src/preview/components/ReferencePreview.tsx +6 -16
  278. package/src/preview/components/UrlComponentPreview.tsx +20 -21
  279. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +6 -5
  280. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +5 -4
  281. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +5 -3
  282. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -3
  283. package/src/preview/property_previews/ArrayOneOfPreview.tsx +6 -4
  284. package/src/preview/property_previews/ArrayPropertyPreview.tsx +5 -3
  285. package/src/preview/property_previews/MapPropertyPreview.tsx +7 -6
  286. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +13 -13
  287. package/src/preview/property_previews/StringPropertyPreview.tsx +2 -2
  288. package/src/preview/util.ts +10 -10
  289. package/src/routes/CustomCMSRoute.tsx +21 -0
  290. package/src/routes/FireCMSRoute.tsx +246 -0
  291. package/src/routes/HomePageRoute.tsx +17 -0
  292. package/src/types/analytics.ts +3 -0
  293. package/src/types/auth.tsx +8 -12
  294. package/src/types/collections.ts +101 -28
  295. package/src/types/customization_controller.tsx +9 -0
  296. package/src/types/datasource.ts +21 -20
  297. package/src/types/dialogs_controller.tsx +7 -3
  298. package/src/types/entities.ts +3 -1
  299. package/src/types/entity_actions.tsx +71 -8
  300. package/src/types/entity_callbacks.ts +18 -18
  301. package/src/types/entity_overrides.tsx +2 -2
  302. package/src/types/export_import.ts +4 -4
  303. package/src/types/fields.tsx +52 -19
  304. package/src/types/firecms.tsx +18 -4
  305. package/src/types/firecms_context.tsx +1 -1
  306. package/src/types/navigation.ts +76 -22
  307. package/src/types/permissions.ts +5 -5
  308. package/src/types/plugins.tsx +50 -9
  309. package/src/types/properties.ts +74 -22
  310. package/src/types/property_config.tsx +1 -2
  311. package/src/types/roles.ts +3 -0
  312. package/src/types/side_dialogs_controller.tsx +15 -0
  313. package/src/types/side_entity_controller.tsx +16 -1
  314. package/src/types/storage.ts +82 -0
  315. package/src/types/user.ts +2 -0
  316. package/src/util/builders.ts +10 -8
  317. package/src/util/callbacks.ts +119 -0
  318. package/src/util/createFormexStub.tsx +62 -0
  319. package/src/util/entities.ts +5 -3
  320. package/src/util/entity_actions.ts +28 -0
  321. package/src/util/entity_cache.ts +204 -0
  322. package/src/util/icon_list.ts +1 -1
  323. package/src/util/icon_synonyms.ts +0 -1
  324. package/src/util/icons.tsx +36 -11
  325. package/src/util/index.ts +3 -0
  326. package/src/util/join_collections.ts +9 -2
  327. package/src/util/make_properties_editable.ts +13 -5
  328. package/src/util/navigation_from_path.ts +33 -12
  329. package/src/util/navigation_utils.ts +135 -19
  330. package/src/util/objects.ts +74 -14
  331. package/src/util/parent_references_from_path.ts +3 -3
  332. package/src/util/permissions.ts +8 -8
  333. package/src/util/property_utils.tsx +17 -6
  334. package/src/util/references.ts +19 -8
  335. package/src/util/resolutions.ts +93 -24
  336. package/src/util/storage.ts +6 -2
  337. package/src/util/useStorageUploadController.tsx +74 -29
  338. package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
  339. package/dist/components/PropertyIdCopyTooltipContent.d.ts +0 -3
  340. package/dist/form/PropertiesForm.d.ts +0 -8
  341. package/dist/form/components/FormikArrayContainer.d.ts +0 -18
  342. package/dist/form/field_bindings/MarkdownFieldBinding.d.ts +0 -9
  343. package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
  344. package/src/components/PropertyIdCopyTooltipContent.tsx +0 -27
  345. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -236
  346. package/src/form/PropertiesForm.tsx +0 -81
  347. package/src/form/components/FormikArrayContainer.tsx +0 -44
  348. package/src/form/field_bindings/MarkdownFieldBinding.tsx +0 -695
  349. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -1,95 +1,112 @@
1
- import { ArchiveIcon, DeleteIcon, FileCopyIcon, KeyboardTabIcon, OpenInNewIcon } from "@firecms/ui";
1
+ import { DeleteIcon, EditIcon, FileCopyIcon } from "@firecms/ui";
2
2
  import { EntityAction } from "../../types";
3
3
  import { DeleteEntityDialog } from "../DeleteEntityDialog";
4
+ import { addRecentId } from "../EntityCollectionView/utils";
5
+ import { navigateToEntity, resolveDefaultSelectedView } from "../../util";
4
6
 
5
7
  export const editEntityAction: EntityAction = {
6
- icon: <KeyboardTabIcon/>,
8
+ icon: <EditIcon size={"small"}/>,
9
+ key: "edit",
7
10
  name: "Edit",
8
11
  collapsed: false,
12
+ isEnabled: ({ entity }) => Boolean(entity),
9
13
  onClick({
10
14
  entity,
11
15
  collection,
12
16
  fullPath,
17
+ fullIdPath,
13
18
  context,
14
19
  highlightEntity,
15
20
  unhighlightEntity,
21
+ openEntityMode
16
22
  }): Promise<void> {
23
+
24
+ if (!entity) {
25
+ throw new Error("INTERNAL: editEntityAction: Entity is undefined");
26
+ }
27
+
17
28
  highlightEntity?.(entity);
29
+
18
30
  context.analyticsController?.onAnalyticsEvent?.("entity_click", {
19
31
  path: entity.path,
20
32
  entityId: entity.id
21
33
  });
22
- const path = collection?.collectionGroup ? entity.path : (fullPath ?? entity.path);
23
- context.sideEntityController.open({
34
+
35
+ if (collection) {
36
+ addRecentId(collection.id, entity.id);
37
+ }
38
+
39
+ const path = collection?.collectionGroup ? entity.path : (fullPath ?? collection?.path ?? entity.path);
40
+ const newFullIdPath = collection?.collectionGroup ? collection.id : (fullIdPath ?? collection?.id ?? entity.path);
41
+ const defaultSelectedView = resolveDefaultSelectedView(
42
+ collection ? collection.defaultSelectedView : undefined,
43
+ {
44
+ status: "existing",
45
+ entityId: entity.id,
46
+ }
47
+ );
48
+ navigateToEntity({
49
+ openEntityMode,
50
+ collection,
24
51
  entityId: entity.id,
25
52
  path,
26
- collection,
27
- updateUrl: true,
53
+ fullIdPath: newFullIdPath,
54
+ sideEntityController: context.sideEntityController,
28
55
  onClose: () => unhighlightEntity?.(entity),
56
+ navigation: context.navigation,
57
+ selectedTab: defaultSelectedView
29
58
  });
59
+
30
60
  return Promise.resolve(undefined);
31
61
  }
32
62
  }
33
63
 
34
64
  export const copyEntityAction: EntityAction = {
35
- icon: <FileCopyIcon/>,
65
+ icon: <FileCopyIcon size={"small"}/>,
36
66
  name: "Copy",
67
+ key: "copy",
68
+ isEnabled: ({ entity }) => Boolean(entity),
37
69
  onClick({
38
70
  entity,
39
71
  collection,
40
72
  context,
73
+ fullPath,
41
74
  highlightEntity,
42
75
  unhighlightEntity,
76
+ openEntityMode
43
77
  }): Promise<void> {
78
+ if (!entity) {
79
+ throw new Error("INTERNAL: copyEntityAction: Entity is undefined");
80
+ }
44
81
  highlightEntity?.(entity);
45
82
  context.analyticsController?.onAnalyticsEvent?.("copy_entity_click", {
46
83
  path: entity.path,
47
84
  entityId: entity.id
48
85
  });
49
- context.sideEntityController.open({
86
+
87
+ const path = collection?.collectionGroup ? collection.path : (fullPath ?? collection?.path ?? entity.path);
88
+ const fullIdPath = collection?.collectionGroup ? collection.id : (fullPath ?? collection?.id ?? entity.path);
89
+ navigateToEntity({
90
+ openEntityMode,
91
+ collection,
50
92
  entityId: entity.id,
51
- path: entity.path,
93
+ path,
94
+ fullIdPath,
52
95
  copy: true,
53
- collection,
54
- updateUrl: true,
96
+ sideEntityController: context.sideEntityController,
55
97
  onClose: () => unhighlightEntity?.(entity),
98
+ navigation: context.navigation
56
99
  });
57
- return Promise.resolve(undefined);
58
- }
59
- }
60
100
 
61
- export const archiveEntityAction: EntityAction = {
62
- icon: <ArchiveIcon/>,
63
- name: "Archive",
64
- onClick({
65
- entity,
66
- collection,
67
- context: {
68
- dataSource,
69
- }
70
- }): Promise<void> {
71
- // Add your code here
72
- return Promise.resolve(undefined);
73
- }
74
- }
75
-
76
- export const openWebsiteAction: EntityAction = {
77
- icon: <OpenInNewIcon/>,
78
- name: "See in website",
79
- onClick({
80
- entity,
81
- collection,
82
- context,
83
- }): Promise<void> {
84
- // open a new tab
85
- window.open(`https://example.com/${entity.id}`, "_blank");
86
101
  return Promise.resolve(undefined);
87
102
  }
88
103
  }
89
104
 
90
105
  export const deleteEntityAction: EntityAction = {
91
- icon: <DeleteIcon/>,
106
+ icon: <DeleteIcon size={"small"}/>,
92
107
  name: "Delete",
108
+ key: "delete",
109
+ isEnabled: ({ entity }) => Boolean(entity),
93
110
  onClick({
94
111
  entity,
95
112
  fullPath,
@@ -97,8 +114,11 @@ export const deleteEntityAction: EntityAction = {
97
114
  context,
98
115
  selectionController,
99
116
  onCollectionChange,
100
- sideEntityController
117
+ navigateBack
101
118
  }): Promise<void> {
119
+ if (!entity) {
120
+ throw new Error("INTERNAL: deleteEntityAction: Entity is undefined");
121
+ }
102
122
  const { closeDialog } = context.dialogsController.open({
103
123
  key: "delete_entity_dialog_" + entity.id,
104
124
  Component: ({ open }) => {
@@ -116,7 +136,7 @@ export const deleteEntityAction: EntityAction = {
116
136
  });
117
137
  selectionController?.setSelectedEntities(selectionController.selectedEntities.filter(e => e.id !== entity.id));
118
138
  onCollectionChange?.();
119
- sideEntityController?.close();
139
+ navigateBack?.();
120
140
  }}
121
141
  onClose={closeDialog}/>;
122
142
  }
@@ -1,6 +1,6 @@
1
1
  export * from "./types";
2
2
  export * from "./useDebouncedData";
3
3
  export * from "./useColumnsIds";
4
- export * from "./useDataSourceEntityCollectionTableController";
4
+ export * from "./useDataSourceTableController";
5
5
  export * from "./useTableSearchHelper";
6
6
  export * from "./default_entity_actions";
@@ -3,7 +3,7 @@ import { EntityCollection, ResolvedEntityCollection, ResolvedProperty } from "..
3
3
  import { getSubcollectionColumnId } from "../EntityCollectionTable/internal/common";
4
4
  import { PropertyColumnConfig } from "../EntityCollectionTable/EntityCollectionTableProps";
5
5
 
6
- const COLLECTION_GROUP_PARENT_ID = "collectionGroupParent";
6
+ export const COLLECTION_GROUP_PARENT_ID = "collectionGroupParent";
7
7
 
8
8
  export function useColumnIds<M extends Record<string, any>>(collection: ResolvedEntityCollection<M>, includeSubcollections: boolean): PropertyColumnConfig[] {
9
9
  return useMemo(() => {
@@ -0,0 +1,420 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from "react";
2
+
3
+ import { useDataSource, useFireCMSContext, useNavigationController } from "../../hooks";
4
+ import { useDataOrder } from "../../hooks/data/useDataOrder";
5
+ import {
6
+ Entity,
7
+ EntityCollection,
8
+ EntityReference,
9
+ EntityTableController,
10
+ FilterValues,
11
+ FireCMSContext,
12
+ SelectedCellProps,
13
+ User,
14
+ WhereFilterOp
15
+ } from "../../types";
16
+ import { useDebouncedData } from "./useDebouncedData";
17
+ import { ScrollRestorationController } from "./useScrollRestoration";
18
+
19
+ const DEFAULT_PAGE_SIZE = 50;
20
+
21
+ export type DataSourceTableControllerProps<M extends Record<string, any> = any> = {
22
+ /**
23
+ * Full path where the data of this table is located
24
+ */
25
+ fullPath: string;
26
+ /**
27
+ * The collection that is represented by this config.
28
+ */
29
+ collection: EntityCollection<M>;
30
+ /**
31
+ * List of entities that will be displayed on top, no matter the ordering.
32
+ * This is used for reference fields selection
33
+ */
34
+ entitiesDisplayedFirst?: Entity<M>[];
35
+
36
+ lastDeleteTimestamp?: number;
37
+
38
+ /**
39
+ * Force filter to be applied to the table.
40
+ */
41
+ forceFilter?: FilterValues<string>;
42
+
43
+ scrollRestoration?: ScrollRestorationController;
44
+
45
+ /**
46
+ * When set to true the filters and sort will be updated in the URL
47
+ */
48
+ updateUrl?: boolean;
49
+
50
+ }
51
+
52
+ /**
53
+ * Use this hook to build a controller for the {@link EntityCollectionTable}.
54
+ * This controller is bound to data in a path in your specified datasource.
55
+ *
56
+ * Note that you can build your own hook returning a {@link EntityTableController}
57
+ * if you would like to display different data.
58
+ *
59
+ * @param fullPath
60
+ * @param collection
61
+ * @param scrollRestoration
62
+ * @param entitiesDisplayedFirst
63
+ * @param lastDeleteTimestamp
64
+ * @param forceFilterFromProps
65
+ * @param updateUrl
66
+ */
67
+ export function useDataSourceTableController<M extends Record<string, any> = any, USER extends User = User>(
68
+ {
69
+ fullPath,
70
+ collection,
71
+ scrollRestoration,
72
+ entitiesDisplayedFirst,
73
+ lastDeleteTimestamp,
74
+ forceFilter: forceFilterFromProps,
75
+ updateUrl
76
+ }: DataSourceTableControllerProps<M>)
77
+ : EntityTableController<M> {
78
+
79
+ const {
80
+ initialFilter,
81
+ initialSort,
82
+ forceFilter: forceFilterFromCollection
83
+ } = collection;
84
+
85
+ const [popupCell, setPopupCell] = React.useState<SelectedCellProps<M> | undefined>(undefined);
86
+ const navigation = useNavigationController();
87
+ const dataSource = useDataSource(collection);
88
+ const resolvedPath = useMemo(() => navigation.resolveIdsFrom(fullPath), [fullPath, navigation.resolveIdsFrom]);
89
+
90
+ const forceFilter = forceFilterFromProps ?? forceFilterFromCollection;
91
+ const paginationEnabled = collection.pagination === undefined || Boolean(collection.pagination);
92
+ const pageSize = typeof collection.pagination === "number" ? collection.pagination : DEFAULT_PAGE_SIZE;
93
+
94
+ const [searchString, setSearchString] = React.useState<string | undefined>();
95
+
96
+ const checkFilterCombination = useCallback((filterValues: FilterValues<any>,
97
+ sortBy?: [string, "asc" | "desc"]) => {
98
+ if (!dataSource.isFilterCombinationValid)
99
+ return true;
100
+ return dataSource.isFilterCombinationValid({
101
+ path: resolvedPath,
102
+ collection,
103
+ filterValues,
104
+ sortBy
105
+ })
106
+ }, []);
107
+
108
+ const onScroll = ({
109
+ scrollOffset
110
+ }: {
111
+ scrollOffset: number
112
+ }) => {
113
+ if (scrollRestoration) {
114
+ scrollRestoration.updateCollectionScroll({
115
+ fullPath: resolvedPath,
116
+ scrollOffset,
117
+ data: rawData,
118
+ filters: filterValues
119
+ });
120
+ }
121
+ }
122
+
123
+ const initialSortInternal = useMemo(() => {
124
+ if (initialSort && forceFilter && !checkFilterCombination(forceFilter, initialSort)) {
125
+ console.warn("Initial sort is not compatible with the force filter. Ignoring initial sort");
126
+ return undefined;
127
+ }
128
+ return initialSort;
129
+ }, [initialSort, forceFilter]);
130
+
131
+ const {
132
+ filterValues: initialFilterUrl,
133
+ sortBy: initialSortUrl,
134
+ } = parseFilterAndSort(window.location.search);
135
+
136
+ const [filterValues, setFilterValues] = React.useState<FilterValues<Extract<keyof M, string>> | undefined>(forceFilter ?? (updateUrl ? initialFilterUrl : undefined) ?? initialFilter ?? undefined);
137
+ const [sortBy, setSortBy] = React.useState<[Extract<keyof M, string>, "asc" | "desc"] | undefined>((updateUrl ? initialSortUrl : undefined) ?? initialSortInternal);
138
+
139
+ useUpdateUrl(filterValues, sortBy, searchString, updateUrl);
140
+
141
+ const collectionScroll = scrollRestoration?.getCollectionScroll(fullPath, filterValues);
142
+ const initialItemCount = collectionScroll?.data.length ?? pageSize;
143
+
144
+ useEffect(() => {
145
+ if (scrollRestoration) {
146
+ scrollRestoration.updateCollectionScroll({
147
+ fullPath: resolvedPath,
148
+ scrollOffset: collectionScroll?.scrollOffset ?? 0,
149
+ data: rawData,
150
+ filters: filterValues
151
+ });
152
+ }
153
+ }, []);
154
+
155
+ const [itemCount, setItemCount] = React.useState<number | undefined>(paginationEnabled ? initialItemCount : undefined);
156
+
157
+ const sortByProperty = sortBy ? sortBy[0] : undefined;
158
+ const currentSort = sortBy ? sortBy[1] : undefined;
159
+
160
+ const context: FireCMSContext<USER> = useFireCMSContext();
161
+
162
+ const [rawData, setRawData] = useState<Entity<M>[]>(collectionScroll?.data ?? []);
163
+
164
+ const [dataLoading, setDataLoading] = useState<boolean>(false);
165
+ const [dataLoadingError, setDataLoadingError] = useState<Error | undefined>();
166
+ const [noMoreToLoad, setNoMoreToLoad] = useState<boolean>(false);
167
+
168
+ const clearFilter = useCallback(() => setFilterValues(forceFilter ?? undefined), [forceFilter]);
169
+
170
+ const updateFilterValues = useCallback((updatedFilter: FilterValues<Extract<keyof M, string>> | undefined) => {
171
+ if (forceFilter) {
172
+ console.warn("Filter is not compatible with the force filter. Ignoring filter");
173
+ return;
174
+ }
175
+ if (updatedFilter && Object.keys(updatedFilter).length === 0) {
176
+ setFilterValues(undefined);
177
+ } else {
178
+ setFilterValues(updatedFilter);
179
+ }
180
+ }, [forceFilter]);
181
+
182
+ useEffect(() => {
183
+
184
+ setDataLoading(true);
185
+
186
+ const onEntitiesUpdate = async (entities: Entity<M>[]) => {
187
+ if (collection.callbacks?.onFetch) {
188
+ try {
189
+ entities = await Promise.all(
190
+ entities.map((entity) =>
191
+ collection.callbacks!.onFetch!({
192
+ collection,
193
+ path: resolvedPath,
194
+ entity,
195
+ context
196
+ })));
197
+ } catch (e: any) {
198
+ console.error(e);
199
+ }
200
+ }
201
+ setDataLoading(false);
202
+ setDataLoadingError(undefined);
203
+ setRawData(entities.map(e => ({
204
+ ...e,
205
+ // values: sanitizeData(e.values, resolvedCollection.properties)
206
+ })));
207
+ setNoMoreToLoad(!itemCount || entities.length < itemCount);
208
+ };
209
+
210
+ const onError = (error: Error) => {
211
+ console.error("ERROR", error);
212
+ setDataLoading(false);
213
+ setRawData([]);
214
+ setDataLoadingError(error);
215
+ };
216
+
217
+ if (dataSource.listenCollection) {
218
+ return dataSource.listenCollection<M>({
219
+ path: resolvedPath,
220
+ collection,
221
+ onUpdate: onEntitiesUpdate,
222
+ onError,
223
+ searchString,
224
+ filter: filterValues,
225
+ limit: itemCount,
226
+ startAfter: undefined,
227
+ orderBy: sortByProperty,
228
+ order: currentSort
229
+ });
230
+ } else {
231
+ dataSource.fetchCollection<M>({
232
+ path: resolvedPath,
233
+ collection,
234
+ searchString,
235
+ filter: filterValues,
236
+ limit: itemCount,
237
+ startAfter: undefined,
238
+ orderBy: sortByProperty,
239
+ order: currentSort
240
+ })
241
+ .then(onEntitiesUpdate)
242
+ .catch(onError);
243
+ return () => {
244
+ };
245
+ }
246
+ }, [resolvedPath, itemCount, currentSort, sortByProperty, filterValues, searchString]);
247
+
248
+ const orderedData = useDataOrder({
249
+ data: rawData,
250
+ entitiesDisplayedFirst
251
+ });
252
+
253
+ // hack to fix Firestore listeners firing with incomplete data
254
+ const data = useDebouncedData(orderedData, {
255
+ filterValues,
256
+ sortBy,
257
+ searchString,
258
+ lastDeleteTimestamp
259
+ });
260
+
261
+ return {
262
+ data,
263
+ dataLoading,
264
+ noMoreToLoad,
265
+ dataLoadingError,
266
+ filterValues,
267
+ setFilterValues: updateFilterValues,
268
+ sortBy,
269
+ setSortBy,
270
+ searchString,
271
+ setSearchString,
272
+ clearFilter,
273
+ itemCount,
274
+ setItemCount,
275
+ initialScroll: collectionScroll?.scrollOffset,
276
+ onScroll,
277
+ paginationEnabled,
278
+ pageSize,
279
+ checkFilterCombination,
280
+ popupCell,
281
+ setPopupCell
282
+ }
283
+ }
284
+
285
+ function useUpdateUrl<M extends Record<string, any> = any>(
286
+ filterValues: FilterValues<Extract<keyof M, string>> | undefined,
287
+ sortBy: [Extract<keyof M, string>, "asc" | "desc"] | undefined,
288
+ searchString: string | undefined,
289
+ updateUrl: boolean | undefined
290
+ ) {
291
+
292
+ useEffect(() => {
293
+ if (updateUrl) {
294
+ const newUrl = encodeFilterAndSort(filterValues, sortBy);
295
+ const search = searchString ? `&search=${encodeURIComponent(searchString)}` : "";
296
+ const state = `${newUrl}${search}`;
297
+ const hash = window.location.hash;
298
+ if (state === "")
299
+ window.history.replaceState({}, "", `${window.location.pathname}${hash}`);
300
+ else
301
+ window.history.replaceState({}, "", `?${state}${hash}`);
302
+ }
303
+ }, [filterValues, sortBy, searchString, updateUrl]);
304
+ }
305
+
306
+ function encodeFilterAndSort(filterValues?: FilterValues<string>, sortBy?: [string, "asc" | "desc"] | undefined) {
307
+ const entries: Record<string, string> = {};
308
+ if (sortBy) {
309
+ entries["__sort"] = encodeURIComponent(sortBy[0]);
310
+ entries["__sort_order"] = encodeURIComponent(sortBy[1]);
311
+ }
312
+ if (filterValues) {
313
+ Object.entries(filterValues).forEach(([key, value]) => {
314
+ if (value) {
315
+ const [op, val] = value;
316
+ let encodedValue: any = val;
317
+ try {
318
+ if (typeof val === "object") {
319
+ if (val instanceof Date) {
320
+ encodedValue = val.toISOString();
321
+ } else if (Array.isArray(val)) {
322
+ encodedValue = JSON.stringify(val, (key, value) => {
323
+ if (value instanceof EntityReference) {
324
+ return encodeRef(value);
325
+ }
326
+ return value;
327
+ });
328
+ } else if (val instanceof EntityReference) {
329
+ encodedValue = encodeRef(val);
330
+ }
331
+ }
332
+ } catch (e) {
333
+ encodedValue = val;
334
+ }
335
+ if (encodedValue !== undefined) {
336
+ entries[encodeURIComponent(`${key}_op`)] = encodeURIComponent(op);
337
+ entries[encodeURIComponent(`${key}_value`)] = encodedValue ? encodeURIComponent(encodedValue.toString()) : "null";
338
+ }
339
+ }
340
+ });
341
+ }
342
+ if (!Object.keys(entries).length) {
343
+ return "";
344
+ }
345
+ return Object.entries(entries).map(([key, value]) => `${key}=${value}`).join("&");
346
+ }
347
+
348
+ function parseFilterAndSort<M>(search: string): {
349
+ filterValues: FilterValues<string> | undefined,
350
+ sortBy?: [Extract<keyof M, string>, "asc" | "desc"]
351
+ } {
352
+ const entries = new URLSearchParams(search);
353
+ const filterValues: FilterValues<string> = {};
354
+ let sortBy: [string, "asc" | "desc"] | undefined = undefined;
355
+ entries.forEach((value, key) => {
356
+ if (key === "__sort") {
357
+ sortBy = [decodeURIComponent(value), entries.get("__sort_order") as "asc" | "desc"];
358
+ } else if (key.endsWith("_op")) {
359
+ const field = key.replace("_op", "");
360
+ const filterOp = decodeURIComponent(value) as WhereFilterOp;
361
+ const filterValStr = entries.get(`${field}_value`);
362
+ if (filterValStr !== null) {
363
+ filterValues[field] = [filterOp, decodeString(filterValStr)];
364
+ }
365
+ }
366
+ });
367
+
368
+ return {
369
+ filterValues: Object.keys(filterValues).length ? filterValues : undefined,
370
+ sortBy
371
+ }
372
+ }
373
+
374
+ function isDate(dateString: string): boolean {
375
+ // Define a regex pattern that matches the exact date format: 2025-01-07T23:00:00.000Z
376
+ const regexPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
377
+
378
+ // Test the dateString against the regex pattern
379
+ if (!regexPattern.test(dateString)) {
380
+ return false;
381
+ }
382
+
383
+ // If the regex matches, further validate if it is a valid UTC date
384
+ const date = new Date(dateString);
385
+ return date.toISOString() === dateString;
386
+ }
387
+
388
+ function encodeRef(val: EntityReference) {
389
+ return `ref::${val.path}/${val.id}`;
390
+ }
391
+
392
+ function decodeString(val: string): EntityReference | Date | string {
393
+ let parsedFilterVal: any = val;
394
+ if (isDate(val)) {
395
+ try {
396
+ parsedFilterVal = new Date(val);
397
+ } catch (e) {
398
+ // ignore
399
+ }
400
+ }
401
+ if (typeof parsedFilterVal === "string") {
402
+ try {
403
+ parsedFilterVal = JSON.parse(parsedFilterVal, (key, value) => {
404
+ if (typeof value === "string" && value.startsWith("ref::")) {
405
+ const [path, id] = value.substring(5).split("/");
406
+ return new EntityReference(id, path);
407
+ }
408
+ return value;
409
+ });
410
+ } catch (e) {
411
+ // ignore
412
+ }
413
+ }
414
+
415
+ if (typeof parsedFilterVal === "string" && parsedFilterVal.startsWith("ref::")) {
416
+ const [path, id] = parsedFilterVal.substring(5).split("/");
417
+ return new EntityReference(id, path);
418
+ }
419
+ return parsedFilterVal;
420
+ }
@@ -0,0 +1,20 @@
1
+ import { useCallback, useRef } from "react";
2
+
3
+ export function useDebounceCallback<T extends (...args: any[]) => any>(
4
+ callback?: T,
5
+ delay?: number
6
+ ): T {
7
+ const timeoutRef = useRef<number | null>(null);
8
+
9
+ const debouncedCallback = useCallback((...args: Parameters<T>) => {
10
+ if (timeoutRef.current !== null) {
11
+ clearTimeout(timeoutRef.current);
12
+ }
13
+
14
+ timeoutRef.current = window.setTimeout(() => {
15
+ callback?.(...args);
16
+ }, delay ?? 200);
17
+ }, [callback, delay]);
18
+
19
+ return debouncedCallback as T;
20
+ }
@@ -0,0 +1,68 @@
1
+ import { Entity, FilterValues } from "../../types";
2
+
3
+ const collectionScrollCache = new Map<string, { scrollOffset: number, data: Entity<any>[] }>();
4
+
5
+ export type ScrollRestorationController = {
6
+
7
+ getCollectionScroll: (fullPath: string,
8
+ filters?: FilterValues<any>) => {
9
+ scrollOffset: number,
10
+ data: Entity<any>[]
11
+ } | undefined;
12
+
13
+ updateCollectionScroll: (props: {
14
+ fullPath: string,
15
+ scrollOffset: number,
16
+ filters?: FilterValues<any>;
17
+ data: Entity<any>[]
18
+ }) => void;
19
+
20
+ }
21
+
22
+ export function useScrollRestoration(): ScrollRestorationController {
23
+
24
+ const updateCollectionScroll = ({
25
+ fullPath,
26
+ filters,
27
+ scrollOffset,
28
+ data
29
+ }: {
30
+ fullPath: string;
31
+ filters?: FilterValues<any>;
32
+ sort?: [string, "asc" | "desc"];
33
+ scrollOffset: number;
34
+ data: Entity<any>[]
35
+ }) => {
36
+ collectionScrollCache.set(
37
+ createCacheKey(fullPath, filters),
38
+ {
39
+ scrollOffset,
40
+ data
41
+ })
42
+ }
43
+
44
+ const getCollectionScroll = (fullPath: string,
45
+ filters?: FilterValues<any>) => {
46
+ return collectionScrollCache.get(createCacheKey(fullPath, filters));
47
+ }
48
+
49
+ return {
50
+ getCollectionScroll,
51
+ updateCollectionScroll
52
+ }
53
+ }
54
+
55
+ function createCacheKey(fullPath: string, filters?: FilterValues<any>) {
56
+
57
+ if (!filters) {
58
+ return fullPath;
59
+ }
60
+
61
+ // codify the filters into a url friendly string
62
+ const filtersString = filters ? Object.keys(filters).map(key => {
63
+ const value = JSON.stringify(filters[key]);
64
+ return `${key}=${value}`;
65
+ }).join("&") : "";
66
+
67
+ return `${fullPath}?${filtersString}`;
68
+ }