@firecms/core 3.0.0-beta.11 → 3.0.0-beta.13

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 (201) hide show
  1. package/dist/app/Scaffold.d.ts +4 -0
  2. package/dist/components/ArrayContainer.d.ts +14 -3
  3. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
  4. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  5. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +15 -1
  6. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  7. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +8 -0
  8. package/dist/components/EntityJsonPreview.d.ts +3 -0
  9. package/dist/components/PropertyIdCopyTooltip.d.ts +1 -1
  10. package/dist/components/SelectableTable/SelectableTable.d.ts +12 -2
  11. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +1 -1
  12. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  13. package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -0
  14. package/dist/components/common/default_entity_actions.d.ts +0 -2
  15. package/dist/components/common/index.d.ts +1 -1
  16. package/dist/components/common/useColumnsIds.d.ts +1 -0
  17. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +10 -2
  18. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  19. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  20. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  21. package/dist/core/DefaultAppBar.d.ts +8 -2
  22. package/dist/core/EntityEditView.d.ts +36 -21
  23. package/dist/core/EntityEditViewFormActions.d.ts +2 -0
  24. package/dist/core/FireCMS.d.ts +2 -2
  25. package/dist/core/FireCMSRouter.d.ts +4 -0
  26. package/dist/core/NavigationRoutes.d.ts +0 -1
  27. package/dist/core/SideDialogs.d.ts +4 -2
  28. package/dist/core/index.d.ts +2 -1
  29. package/dist/form/EntityForm.d.ts +49 -0
  30. package/dist/form/EntityFormActions.d.ts +17 -0
  31. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  32. package/dist/form/components/FormEntry.d.ts +6 -0
  33. package/dist/form/components/FormLayout.d.ts +5 -0
  34. package/dist/form/components/index.d.ts +2 -0
  35. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  36. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  37. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  38. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  39. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  40. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  41. package/dist/form/index.d.ts +16 -16
  42. package/dist/hooks/data/save.d.ts +1 -1
  43. package/dist/hooks/data/useEntityFetch.d.ts +2 -1
  44. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  45. package/dist/hooks/useBuildNavigationController.d.ts +4 -1
  46. package/dist/hooks/useModeController.d.ts +1 -2
  47. package/dist/index.es.js +15842 -14396
  48. package/dist/index.es.js.map +1 -1
  49. package/dist/index.umd.js +15833 -14389
  50. package/dist/index.umd.js.map +1 -1
  51. package/dist/internal/useBuildDataSource.d.ts +3 -2
  52. package/dist/internal/useBuildSideEntityController.d.ts +3 -3
  53. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  54. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  55. package/dist/routes/FireCMSRoute.d.ts +1 -0
  56. package/dist/routes/HomePageRoute.d.ts +3 -0
  57. package/dist/types/collections.d.ts +36 -10
  58. package/dist/types/datasource.d.ts +2 -2
  59. package/dist/types/dialogs_controller.d.ts +7 -3
  60. package/dist/types/entities.d.ts +1 -1
  61. package/dist/types/entity_actions.d.ts +10 -0
  62. package/dist/types/fields.d.ts +13 -3
  63. package/dist/types/firecms.d.ts +1 -1
  64. package/dist/types/navigation.d.ts +33 -12
  65. package/dist/types/plugins.d.ts +2 -0
  66. package/dist/types/properties.d.ts +40 -4
  67. package/dist/types/side_entity_controller.d.ts +6 -1
  68. package/dist/util/builders.d.ts +1 -1
  69. package/dist/util/createFormexStub.d.ts +2 -0
  70. package/dist/util/entity_actions.d.ts +2 -0
  71. package/dist/util/entity_cache.d.ts +23 -0
  72. package/dist/util/index.d.ts +1 -0
  73. package/dist/util/navigation_from_path.d.ts +6 -1
  74. package/dist/util/navigation_utils.d.ts +13 -1
  75. package/dist/util/objects.d.ts +1 -1
  76. package/dist/util/property_utils.d.ts +2 -2
  77. package/dist/util/references.d.ts +2 -2
  78. package/dist/util/resolutions.d.ts +12 -2
  79. package/package.json +20 -21
  80. package/src/app/Scaffold.tsx +13 -3
  81. package/src/components/ArrayContainer.tsx +60 -24
  82. package/src/components/CircularProgressCenter.tsx +1 -1
  83. package/src/components/DeleteEntityDialog.tsx +5 -3
  84. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +46 -23
  85. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -4
  86. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +18 -1
  87. package/src/components/EntityCollectionTable/index.tsx +1 -1
  88. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
  89. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +10 -13
  90. package/src/components/EntityCollectionView/EntityCollectionView.tsx +105 -47
  91. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -2
  92. package/src/components/EntityJsonPreview.tsx +66 -0
  93. package/src/components/EntityPreview.tsx +10 -3
  94. package/src/components/EntityView.tsx +4 -1
  95. package/src/components/NotFoundPage.tsx +2 -2
  96. package/src/components/PropertyIdCopyTooltip.tsx +2 -3
  97. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +14 -7
  98. package/src/components/SelectableTable/SelectableTable.tsx +18 -3
  99. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +7 -7
  100. package/src/components/UnsavedChangesDialog.tsx +42 -0
  101. package/src/components/VirtualTable/VirtualTable.tsx +48 -3
  102. package/src/components/VirtualTable/VirtualTableProps.tsx +15 -0
  103. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +1 -1
  104. package/src/components/common/default_entity_actions.tsx +37 -37
  105. package/src/components/common/index.ts +1 -1
  106. package/src/components/common/useColumnsIds.tsx +1 -1
  107. package/src/components/common/useDataSourceTableController.tsx +420 -0
  108. package/src/components/common/useDebouncedCallback.tsx +20 -0
  109. package/src/components/common/useScrollRestoration.tsx +68 -0
  110. package/src/contexts/BreacrumbsContext.tsx +38 -0
  111. package/src/contexts/DialogsProvider.tsx +3 -2
  112. package/src/contexts/ModeController.tsx +1 -3
  113. package/src/core/DefaultAppBar.tsx +59 -17
  114. package/src/core/DefaultDrawer.tsx +1 -1
  115. package/src/core/EntityEditView.tsx +354 -1000
  116. package/src/core/EntityEditViewFormActions.tsx +199 -0
  117. package/src/core/EntitySidePanel.tsx +85 -15
  118. package/src/core/FireCMS.tsx +20 -16
  119. package/src/core/FireCMSRouter.tsx +17 -0
  120. package/src/core/NavigationRoutes.tsx +23 -32
  121. package/src/core/SideDialogs.tsx +20 -11
  122. package/src/core/index.tsx +4 -2
  123. package/src/form/EntityForm.tsx +782 -0
  124. package/src/form/EntityFormActions.tsx +169 -0
  125. package/src/form/PropertyFieldBinding.tsx +21 -16
  126. package/src/form/components/FormEntry.tsx +22 -0
  127. package/src/form/components/FormLayout.tsx +16 -0
  128. package/src/form/components/LabelWithIcon.tsx +6 -3
  129. package/src/form/components/StorageUploadProgress.tsx +1 -1
  130. package/src/form/components/index.tsx +2 -0
  131. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +8 -5
  132. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +5 -2
  133. package/src/form/field_bindings/BlockFieldBinding.tsx +5 -3
  134. package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -0
  135. package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
  136. package/src/form/field_bindings/MapFieldBinding.tsx +14 -16
  137. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +15 -8
  138. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +3 -3
  139. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -2
  140. package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -1
  141. package/src/form/field_bindings/RepeatFieldBinding.tsx +13 -4
  142. package/src/form/field_bindings/SelectFieldBinding.tsx +2 -2
  143. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +11 -6
  144. package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -0
  145. package/src/form/field_bindings/TextFieldBinding.tsx +1 -0
  146. package/src/form/index.tsx +16 -32
  147. package/src/form/validation.ts +12 -6
  148. package/src/hooks/data/save.ts +25 -30
  149. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  150. package/src/hooks/data/useEntityFetch.tsx +5 -1
  151. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  152. package/src/hooks/useBuildModeController.tsx +15 -28
  153. package/src/hooks/useBuildNavigationController.tsx +81 -24
  154. package/src/hooks/useFireCMSContext.tsx +0 -30
  155. package/src/hooks/useLargeLayout.tsx +0 -35
  156. package/src/hooks/useModeController.tsx +1 -2
  157. package/src/hooks/useResolvedNavigationFrom.tsx +4 -6
  158. package/src/internal/useBuildDataSource.ts +9 -5
  159. package/src/internal/useBuildSideDialogsController.tsx +3 -2
  160. package/src/internal/useBuildSideEntityController.tsx +135 -88
  161. package/src/internal/useUnsavedChangesDialog.tsx +126 -92
  162. package/src/preview/PropertyPreview.tsx +12 -10
  163. package/src/preview/components/UrlComponentPreview.tsx +17 -18
  164. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +4 -3
  165. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +4 -3
  166. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +4 -2
  167. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -3
  168. package/src/preview/property_previews/ArrayOneOfPreview.tsx +4 -2
  169. package/src/preview/property_previews/ArrayPropertyPreview.tsx +4 -2
  170. package/src/preview/property_previews/MapPropertyPreview.tsx +3 -2
  171. package/src/routes/CustomCMSRoute.tsx +21 -0
  172. package/src/routes/FireCMSRoute.tsx +246 -0
  173. package/src/routes/HomePageRoute.tsx +17 -0
  174. package/src/types/collections.ts +45 -13
  175. package/src/types/datasource.ts +2 -1
  176. package/src/types/dialogs_controller.tsx +7 -3
  177. package/src/types/entities.ts +1 -1
  178. package/src/types/entity_actions.tsx +12 -0
  179. package/src/types/fields.tsx +18 -3
  180. package/src/types/firecms.tsx +1 -1
  181. package/src/types/navigation.ts +41 -16
  182. package/src/types/plugins.tsx +2 -0
  183. package/src/types/properties.ts +48 -4
  184. package/src/types/side_dialogs_controller.tsx +2 -0
  185. package/src/types/side_entity_controller.tsx +11 -1
  186. package/src/util/builders.ts +5 -3
  187. package/src/util/createFormexStub.tsx +62 -0
  188. package/src/util/entity_actions.ts +28 -0
  189. package/src/util/entity_cache.ts +204 -0
  190. package/src/util/index.ts +1 -0
  191. package/src/util/join_collections.ts +6 -1
  192. package/src/util/navigation_from_path.ts +18 -7
  193. package/src/util/navigation_utils.ts +115 -14
  194. package/src/util/objects.ts +9 -2
  195. package/src/util/parent_references_from_path.ts +3 -3
  196. package/src/util/property_utils.tsx +7 -3
  197. package/src/util/references.ts +8 -6
  198. package/src/util/resolutions.ts +41 -12
  199. package/src/util/useStorageUploadController.tsx +60 -23
  200. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -236
  201. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -55,24 +55,23 @@ export function UrlComponentPreview({
55
55
  return <VideoPreview size={size} src={url} interactive={interactive}/>;
56
56
  } else {
57
57
  return (
58
- <a
59
- href={url}
60
- rel="noopener noreferrer"
61
- target="_blank"
62
- onClick={(e) => e.stopPropagation()}
63
- className="flex flex-col items-center justify-center"
64
- style={{
65
- width: getThumbnailMeasure(size),
66
- height: getThumbnailMeasure(size)
67
- }}>
68
- <DescriptionIcon className="text-surface-700 dark:text-surface-300"/>
69
- {hint &&
70
- <Tooltip title={hint}>
71
- <Typography
72
- className="max-w-full truncate rtl text-left"
73
- variant={"caption"}>{hint}</Typography>
74
- </Tooltip>}
75
- </a>
58
+ <Tooltip title={hint}>
59
+ <a
60
+ href={url}
61
+ rel="noopener noreferrer"
62
+ target="_blank"
63
+ onClick={(e) => e.stopPropagation()}
64
+ className="flex flex-col items-center justify-center"
65
+ style={{
66
+ width: getThumbnailMeasure(size),
67
+ height: getThumbnailMeasure(size)
68
+ }}>
69
+ <DescriptionIcon className="text-surface-700 dark:text-surface-300"/>
70
+ {hint && <Typography
71
+ className="max-w-full truncate rtl text-left"
72
+ variant={"caption"}>{hint}</Typography>}
73
+ </a>
74
+ </Tooltip>
76
75
  );
77
76
  }
78
77
  }
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import { ErrorBoundary } from "../../components";
3
- import { useCustomizationController } from "../../hooks";
3
+ import { useAuthController, useCustomizationController } from "../../hooks";
4
4
  import { PropertyPreviewProps } from "../PropertyPreviewProps";
5
5
  import { PropertyPreview } from "../PropertyPreview";
6
6
  import { resolveArrayProperty } from "../../util";
@@ -15,12 +15,13 @@ export function ArrayOfMapsPreview({
15
15
  size,
16
16
  // entity
17
17
  }: PropertyPreviewProps<Record<string, any>[]>) {
18
-
18
+ const authController = useAuthController();
19
19
  const customizationController = useCustomizationController();
20
20
  const property = resolveArrayProperty({
21
21
  propertyKey,
22
22
  property: inputProperty,
23
- propertyConfigs: customizationController.propertyConfigs
23
+ propertyConfigs: customizationController.propertyConfigs,
24
+ authController
24
25
  });
25
26
 
26
27
  if (Array.isArray(property?.of)) {
@@ -1,6 +1,6 @@
1
1
  import { ResolvedReferenceProperty } from "../../types";
2
2
  import { resolveArrayProperty } from "../../util";
3
- import { useCustomizationController } from "../../hooks";
3
+ import { useAuthController, useCustomizationController } from "../../hooks";
4
4
  import { PreviewSize, PropertyPreviewProps } from "../PropertyPreviewProps";
5
5
  import { ReferencePreview } from "../components/ReferencePreview";
6
6
 
@@ -13,12 +13,13 @@ export function ArrayOfReferencesPreview({
13
13
  property: inputProperty,
14
14
  size
15
15
  }: PropertyPreviewProps<any[]>) {
16
-
16
+ const authController = useAuthController();
17
17
  const customizationController = useCustomizationController();
18
18
  const property = resolveArrayProperty({
19
19
  propertyKey,
20
20
  property: inputProperty,
21
- propertyConfigs: customizationController.propertyConfigs
21
+ propertyConfigs: customizationController.propertyConfigs,
22
+ authController
22
23
  });
23
24
 
24
25
  if (Array.isArray(property?.of)) {
@@ -3,7 +3,7 @@ import React from "react";
3
3
  import { resolveArrayProperty } from "../../util";
4
4
  import { ResolvedProperty } from "../../types";
5
5
 
6
- import { useCustomizationController } from "../../hooks";
6
+ import { useAuthController, useCustomizationController } from "../../hooks";
7
7
  import { PreviewSize, PropertyPreviewProps } from "../PropertyPreviewProps";
8
8
  import { PropertyPreview } from "../PropertyPreview";
9
9
  import { ErrorBoundary } from "../../components";
@@ -19,11 +19,13 @@ export function ArrayOfStorageComponentsPreview({
19
19
  size
20
20
  }: PropertyPreviewProps<any[]>) {
21
21
 
22
+ const authController = useAuthController();
22
23
  const customizationController = useCustomizationController();
23
24
  const property = resolveArrayProperty({
24
25
  propertyKey,
25
26
  property: inputProperty,
26
- propertyConfigs: customizationController.propertyConfigs
27
+ propertyConfigs: customizationController.propertyConfigs,
28
+ authController
27
29
  });
28
30
 
29
31
  if (Array.isArray(property.of)) {
@@ -3,7 +3,7 @@ import { ResolvedStringProperty } from "../../types";
3
3
 
4
4
  import { resolveArrayProperty } from "../../util";
5
5
  import { PropertyPreviewProps, StringPropertyPreview } from "../../preview";
6
- import { useCustomizationController } from "../../hooks";
6
+ import { useAuthController, useCustomizationController } from "../../hooks";
7
7
  import { ErrorBoundary } from "../../components";
8
8
 
9
9
  /**
@@ -16,12 +16,13 @@ export function ArrayOfStringsPreview({
16
16
  // entity,
17
17
  size
18
18
  }: PropertyPreviewProps<string[]>) {
19
-
19
+ const authController = useAuthController();
20
20
  const customizationController = useCustomizationController();
21
21
  const property = resolveArrayProperty({
22
22
  propertyKey,
23
23
  property: inputProperty,
24
- propertyConfigs: customizationController.propertyConfigs
24
+ propertyConfigs: customizationController.propertyConfigs,
25
+ authController
25
26
  });
26
27
 
27
28
  if (Array.isArray(property.of)) {
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import { resolveArrayProperty } from "../../util";
3
3
  import { ResolvedProperty } from "../../types";
4
- import { useCustomizationController } from "../../hooks";
4
+ import { useAuthController, useCustomizationController } from "../../hooks";
5
5
  import { PreviewSize, PropertyPreviewProps } from "../PropertyPreviewProps";
6
6
  import { PropertyPreview } from "../PropertyPreview";
7
7
  import { cls, defaultBorderMixin } from "@firecms/ui";
@@ -19,11 +19,13 @@ export function ArrayOneOfPreview({
19
19
  // entity
20
20
  }: PropertyPreviewProps<any[]>) {
21
21
 
22
+ const authController = useAuthController();
22
23
  const customizationController = useCustomizationController();
23
24
  const property = resolveArrayProperty({
24
25
  propertyKey,
25
26
  property: inputProperty,
26
- propertyConfigs: customizationController.propertyConfigs
27
+ propertyConfigs: customizationController.propertyConfigs,
28
+ authController
27
29
  });
28
30
 
29
31
  if (property?.dataType !== "array")
@@ -2,7 +2,7 @@ import React from "react";
2
2
 
3
3
  import { resolveArrayProperty } from "../../util";
4
4
  import { ResolvedProperty } from "../../types";
5
- import { useCustomizationController } from "../../hooks";
5
+ import { useAuthController, useCustomizationController } from "../../hooks";
6
6
  import { PreviewSize, PropertyPreviewProps } from "../PropertyPreviewProps";
7
7
  import { PropertyPreview } from "../PropertyPreview";
8
8
  import { cls, defaultBorderMixin } from "@firecms/ui";
@@ -19,11 +19,13 @@ export function ArrayPropertyPreview({
19
19
  size
20
20
  }: PropertyPreviewProps<any[]>) {
21
21
 
22
+ const authController = useAuthController();
22
23
  const customizationController = useCustomizationController();
23
24
  const property = resolveArrayProperty({
24
25
  propertyKey,
25
26
  property: inputProperty,
26
- propertyConfigs: customizationController.propertyConfigs
27
+ propertyConfigs: customizationController.propertyConfigs,
28
+ authController
27
29
  });
28
30
 
29
31
  if (!property.of) {
@@ -58,6 +58,7 @@ export function MapPropertyPreview<T extends Record<string, any> = Record<string
58
58
  {mapPropertyKeys &&
59
59
  mapPropertyKeys.map((key, index) => {
60
60
  const childProperty = mapProperty.properties![key];
61
+ const isArrayOrMap = childProperty.dataType === "map" || childProperty === "array";
61
62
  return (
62
63
  <div
63
64
  key={`map_preview_table_${key}}`}
@@ -75,7 +76,7 @@ export function MapPropertyPreview<T extends Record<string, any> = Record<string
75
76
  <div
76
77
  className="flex-grow max-w-[75%]">
77
78
  <ErrorBoundary>
78
- {!(childProperty.dataType === "map" || childProperty === "array") &&
79
+ {!isArrayOrMap &&
79
80
  <PropertyPreview
80
81
  propertyKey={key}
81
82
  value={(value)[key]}
@@ -86,7 +87,7 @@ export function MapPropertyPreview<T extends Record<string, any> = Record<string
86
87
  </div>
87
88
  </div>
88
89
 
89
- {(childProperty.dataType === "map" || childProperty === "array") &&
90
+ {isArrayOrMap &&
90
91
  <div className={cls(defaultBorderMixin, "border-l pl-4 ml-2 my-2")}>
91
92
  <PropertyPreview
92
93
  propertyKey={key}
@@ -0,0 +1,21 @@
1
+ import { CMSView } from "../types";
2
+ import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
3
+ import { useEffect } from "react";
4
+
5
+ export function CustomCMSRoute({ cmsView }: {
6
+ cmsView: CMSView
7
+ }) {
8
+
9
+ const breadcrumbs = useBreadcrumbsController();
10
+
11
+ useEffect(() => {
12
+ breadcrumbs.set({
13
+ breadcrumbs: [{
14
+ title: cmsView.name,
15
+ url: cmsView.path
16
+ }]
17
+ });
18
+ }, [cmsView.path]);
19
+
20
+ return cmsView.view;
21
+ }
@@ -0,0 +1,246 @@
1
+ import { Blocker, useBlocker, useLocation } from "react-router";
2
+ import { EntityEditView } from "../core/EntityEditView";
3
+ import { useNavigationController } from "../hooks";
4
+ import { useEffect, useRef, useState } from "react";
5
+ import { useNavigate } from "react-router-dom";
6
+ import {
7
+ getNavigationEntriesFromPath,
8
+ NavigationViewCollectionInternal,
9
+ NavigationViewEntityCustomInternal,
10
+ NavigationViewInternal
11
+ } from "../util/navigation_from_path";
12
+ import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
13
+ import { toArray } from "../util/arrays";
14
+ import { EntityCollectionView, NotFoundPage } from "../components";
15
+ import { UnsavedChangesDialog } from "../components/UnsavedChangesDialog";
16
+ import { EntityCollection } from "../types";
17
+
18
+ export function FireCMSRoute() {
19
+
20
+ const location = useLocation();
21
+ const navigation = useNavigationController();
22
+ const breadcrumbs = useBreadcrumbsController();
23
+
24
+ const hash = location.hash;
25
+ const isSidePanel = hash.includes("#side");
26
+ const isNew = hash.includes("#new") || hash.includes("#new_side");
27
+ const isCopy = hash.includes("#copy");
28
+
29
+ const pathname = location.pathname;
30
+ const navigationPath = navigation.urlPathToDataPath(pathname);
31
+
32
+ const navigationEntries = getNavigationEntriesFromPath({
33
+ path: navigationPath,
34
+ collections: navigation.collections ?? []
35
+ });
36
+
37
+ useEffect(() => {
38
+ breadcrumbs.set({
39
+ breadcrumbs: navigationEntries.map(entry => {
40
+ if (entry.type === "entity") {
41
+ return ({
42
+ title: entry.entityId,
43
+ url: navigation.buildUrlCollectionPath(entry.fullPath)
44
+ });
45
+ } else if (entry.type === "custom_view") {
46
+ return ({
47
+ title: entry.view.name,
48
+ url: navigation.buildUrlCollectionPath(entry.fullPath)
49
+ });
50
+ } else if (entry.type === "collection") {
51
+ return ({
52
+ title: entry.collection.name,
53
+ url: navigation.buildUrlCollectionPath(entry.fullPath)
54
+ });
55
+ } else {
56
+ throw new Error("Unexpected navigation entry type");
57
+ }
58
+ })
59
+ });
60
+ }, [navigationEntries.map(entry => entry.path).join(",")]);
61
+
62
+ if (isNew) {
63
+ return <EntityFullScreenRoute
64
+ pathname={pathname}
65
+ navigationEntries={navigationEntries}
66
+ isNew={true}
67
+ isCopy={false}
68
+ />;
69
+ }
70
+
71
+ if (navigationEntries.length === 1 && navigationEntries[0].type === "collection") {
72
+ let collection: EntityCollection<any> | undefined;
73
+ collection = navigation.getCollectionById(navigationEntries[0].id);
74
+ if (!collection)
75
+ collection = navigation.getCollection(navigationEntries[0].path);
76
+ if (!collection)
77
+ return null;
78
+ return <EntityCollectionView
79
+ key={`collection_view_${collection.id ?? collection.path}`}
80
+ isSubCollection={false}
81
+ parentCollectionIds={[]}
82
+ fullPath={collection.id}
83
+ updateUrl={true}
84
+ {...collection}
85
+ Actions={toArray(collection.Actions)}/>
86
+ }
87
+
88
+ if (isSidePanel) {
89
+ const lastCollectionEntry = navigationEntries.findLast((entry) => entry.type === "collection");
90
+ if (lastCollectionEntry) {
91
+ let collection: EntityCollection<any> | undefined;
92
+ const firstEntry = navigationEntries[0] as NavigationViewCollectionInternal<any>;
93
+ collection = navigation.getCollectionById(firstEntry.id);
94
+ if (!collection)
95
+ collection = navigation.getCollection(firstEntry.path);
96
+ if (!collection)
97
+ return null;
98
+ return <EntityCollectionView
99
+ key={`collection_view_${collection.id ?? collection.path}`}
100
+ fullIdPath={collection.id}
101
+ isSubCollection={false}
102
+ parentCollectionIds={[]}
103
+ fullPath={collection.id}
104
+ updateUrl={true}
105
+ {...collection}
106
+ Actions={toArray(collection.Actions)}/>;
107
+ }
108
+ }
109
+
110
+ return <EntityFullScreenRoute
111
+ pathname={pathname}
112
+ navigationEntries={navigationEntries}
113
+ isNew={isNew}
114
+ isCopy={isCopy}
115
+ />;
116
+
117
+ }
118
+
119
+ function getSelectedTabFromUrl(isNew: boolean, lastCustomView: NavigationViewCollectionInternal<any> | NavigationViewEntityCustomInternal<any> | undefined) {
120
+ if (isNew) {
121
+ return undefined;
122
+ } else if (lastCustomView) {
123
+ if (lastCustomView.type === "custom_view") {
124
+ return lastCustomView.view.key;
125
+ } else if (lastCustomView.type === "collection") {
126
+ return lastCustomView.id ?? lastCustomView.path;
127
+ }
128
+ }
129
+ return undefined;
130
+ }
131
+
132
+ function EntityFullScreenRoute({
133
+ pathname,
134
+ navigationEntries,
135
+ isNew,
136
+ isCopy
137
+ }: {
138
+ pathname: string;
139
+ navigationEntries: NavigationViewInternal[],
140
+ isNew: boolean,
141
+ isCopy: boolean
142
+ }) {
143
+
144
+ const navigation = useNavigationController();
145
+ const navigate = useNavigate();
146
+
147
+ const navigationPath = navigation.urlPathToDataPath(pathname);
148
+
149
+ // is navigating away blocked
150
+ const blocked = useRef(false);
151
+
152
+ const lastEntityEntry = navigationEntries.findLast((entry) => entry.type === "entity");
153
+ const navigationEntriesAfterEntity = lastEntityEntry ? navigationEntries.slice(navigationEntries.indexOf(lastEntityEntry) + 1) : [];
154
+
155
+ const lastCustomView = navigationEntriesAfterEntity.findLast(
156
+ (entry) => entry.type === "custom_view" || entry.type === "collection"
157
+ ) as NavigationViewCollectionInternal<any> | NavigationViewEntityCustomInternal<any> | undefined;
158
+
159
+ const entityId = lastEntityEntry?.entityId;
160
+
161
+ const urlTab = getSelectedTabFromUrl(isNew, lastCustomView);
162
+ const [selectedTab, setSelectedTab] = useState<string | undefined>(urlTab);
163
+
164
+ const parentCollectionIds = navigation.getParentCollectionIds(navigationPath);
165
+ useEffect(() => {
166
+ if (urlTab !== selectedTab) {
167
+ setSelectedTab(urlTab);
168
+ }
169
+ }, [urlTab]);
170
+
171
+ const basePath = !entityId || isNew
172
+ ? pathname
173
+ : pathname.substring(0, pathname.lastIndexOf(`/${entityId}`));
174
+
175
+ const entityPath = basePath + `/${entityId}`;
176
+
177
+ let blocker: Blocker | undefined = undefined;
178
+ try {
179
+ blocker = useBlocker(({
180
+ nextLocation
181
+ }) => {
182
+ if (nextLocation.pathname.startsWith(entityPath))
183
+ return false;
184
+ return blocked.current;
185
+ });
186
+ } catch (e) {
187
+ // console.warn("Blocker not available, navigation will not be blocked");
188
+ }
189
+
190
+ const lastCollectionEntry = navigationEntries.findLast((entry) => entry.type === "collection");
191
+
192
+ if (isNew && !lastCollectionEntry) {
193
+ throw new Error("INTERNAL: No collection found in the navigation");
194
+ }
195
+
196
+ if (!isNew && !lastEntityEntry) {
197
+ return <NotFoundPage/>;
198
+ }
199
+
200
+ const collection = isNew ? lastCollectionEntry!.collection : lastEntityEntry!.parentCollection;
201
+ const fullIdPath = isNew ? lastCollectionEntry!.path : lastEntityEntry!.path;
202
+ const collectionPath = navigation.resolveIdsFrom(fullIdPath);
203
+
204
+ return <>
205
+ <EntityEditView
206
+ key={collection.id + "_" + (isNew ? "new" : (isCopy ? entityId + "_copy" : entityId))}
207
+ entityId={isNew ? undefined : entityId}
208
+ fullIdPath={fullIdPath}
209
+ collection={collection}
210
+ layout={"full_screen"}
211
+ path={collectionPath}
212
+ copy={isCopy}
213
+ selectedTab={selectedTab ?? undefined}
214
+ onValuesModified={(modified) => blocked.current = modified}
215
+ onSaved={(params) => {
216
+ const newSelectedTab = params.selectedTab;
217
+ const newEntityId = params.entityId;
218
+ if (newSelectedTab) {
219
+ navigate(`${basePath}/${newEntityId}/${newSelectedTab}`, { replace: true });
220
+ } else {
221
+ navigate(`${basePath}/${newEntityId}`, { replace: true });
222
+ }
223
+ }}
224
+ onTabChange={(params) => {
225
+ setSelectedTab(params.selectedTab);
226
+ if (isNew) {
227
+ return;
228
+ }
229
+ const newSelectedTab = params.selectedTab;
230
+ if (newSelectedTab) {
231
+ navigate(`${basePath}/${entityId}/${newSelectedTab}`, { replace: true });
232
+ } else {
233
+ navigate(`${basePath}/${entityId}`, { replace: true });
234
+ }
235
+ }}
236
+ parentCollectionIds={parentCollectionIds}
237
+ />
238
+
239
+ <UnsavedChangesDialog
240
+ open={blocker?.state === "blocked"}
241
+ handleOk={() => blocker?.proceed?.()}
242
+ handleCancel={() => blocker?.reset?.()}
243
+ body={"You have unsaved changes in this entity."}/>
244
+
245
+ </>;
246
+ }
@@ -0,0 +1,17 @@
1
+ import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
2
+ import { useEffect } from "react";
3
+
4
+ export function HomePageRoute({ children }: {
5
+ children: React.ReactNode;
6
+ }) {
7
+
8
+ const breadcrumbs = useBreadcrumbsController();
9
+
10
+ useEffect(() => {
11
+ breadcrumbs.set({
12
+ breadcrumbs: []
13
+ });
14
+ }, []);
15
+
16
+ return children;
17
+ }
@@ -84,6 +84,11 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
84
84
  */
85
85
  properties: PropertiesOrBuilders<M>;
86
86
 
87
+ /**
88
+ * Default preview properties displayed when this collection is referenced to.
89
+ */
90
+ previewProperties?: string[];
91
+
87
92
  /**
88
93
  * Title property of the entity. This is the property that will be used
89
94
  * as the title in entity related views and references.
@@ -92,11 +97,10 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
92
97
  titleProperty?: keyof M;
93
98
 
94
99
  /**
95
- * Can this collection be edited by the end user.
96
- * Defaults to `true`.
97
- * Keep in mind that you can also set this prop to individual properties.
100
+ * When editing an entity, you can choose to open the entity in a side dialog
101
+ * or in a full screen dialog. Defaults to `full_screen`.
98
102
  */
99
- editable?: boolean;
103
+ openEntityMode?: "side_panel" | "full_screen";
100
104
 
101
105
  /**
102
106
  * Order in which the properties are displayed.
@@ -119,7 +123,7 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
119
123
  * `hidden` in the property definition,will be ignored.
120
124
  * `propertiesOrder` has precedence over `hidden`.
121
125
  */
122
- propertiesOrder?: (Extract<keyof M, string> | `subcollection:${string}`)[];
126
+ propertiesOrder?: (Extract<keyof M, string> | string | `subcollection:${string}` | "collectionGroupParent")[];
123
127
 
124
128
  /**
125
129
  * If enabled, content is loaded in batches. If `false` all entities in the
@@ -211,6 +215,7 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
211
215
  * Force a filter in this view. If applied, the rest of the filters will
212
216
  * be disabled. Filters applied with this prop cannot be changed.
213
217
  * e.g. `forceFilter: { age: [">=", 18] }`
218
+ * e.g. `forceFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
214
219
  */
215
220
  forceFilter?: FilterValues<Extract<keyof M, string>>;
216
221
 
@@ -218,6 +223,7 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
218
223
  * Initial filters applied to the collection this collection is related to.
219
224
  * Defaults to none. Filters applied with this prop can be changed.
220
225
  * e.g. `initialFilter: { age: [">=", 18] }`
226
+ * e.g. `initialFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
221
227
  */
222
228
  initialFilter?: FilterValues<Extract<keyof M, string>>; // setting FilterValues<M> can break defining collections by code
223
229
 
@@ -313,9 +319,24 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
313
319
  sideDialogWidth?: number | string;
314
320
 
315
321
  /**
316
- * Default preview properties displayed when this collection is referenced to.
322
+ * Can this collection configuration be edited by the end user.
323
+ * Defaults to `true`.
324
+ * Keep in mind that you can also set this prop to individual properties.
325
+ * This prop has only effect if you are using the collection editor.
317
326
  */
318
- previewProperties?: string[];
327
+ editable?: boolean;
328
+
329
+ /**
330
+ * If set to true, the default values of the properties will be applied
331
+ * to the entity every time the entity is updated (not only when created).
332
+ * Defaults to false.
333
+ */
334
+ alwaysApplyDefaultValues?: boolean;
335
+
336
+ /**
337
+ * If set to true, a tab including the JSON representation of the entity will be included.
338
+ */
339
+ includeJsonView?: boolean;
319
340
  }
320
341
 
321
342
  /**
@@ -491,21 +512,26 @@ export type EntityCustomView<M extends Record<string, any> = any> =
491
512
  /**
492
513
  * Key of this custom view.
493
514
  */
494
- key: string,
515
+ key: string;
516
+
495
517
  /**
496
518
  * Name of this custom view.
497
519
  */
498
- name: string,
520
+ name: string;
521
+
499
522
  /**
500
- * If set to true, the actions of the entity will be included in the
501
- * bottom of the panel (save buttons, delete buttons, etc.)
523
+ * If set to true, the actions of the entity (save, discard,delete) will be
524
+ * included in the view. By default the actions are located in the right or bottom,
525
+ * based on the screen size. You can force the actions to be located at the bottom
526
+ * by setting this prop to "bottom".
502
527
  */
503
- includeActions?: boolean;
528
+ includeActions?: boolean | "bottom";
529
+
504
530
  /**
505
531
  * Builder for rendering the custom view
506
532
  */
507
533
  Builder?: React.ComponentType<EntityCustomViewParams<M>>;
508
- }
534
+ };
509
535
 
510
536
  /**
511
537
  * Parameters passed to the builder in charge of rendering a custom panel for
@@ -570,6 +596,12 @@ export type EntityTableController<M extends Record<string, any> = any> = {
570
596
  clearFilter?: () => void;
571
597
  itemCount?: number;
572
598
  setItemCount?: (itemCount: number) => void;
599
+ initialScroll?: number;
600
+ onScroll?: (props: {
601
+ scrollDirection: "forward" | "backward",
602
+ scrollOffset: number,
603
+ scrollUpdateWasRequested: boolean
604
+ }) => void;
573
605
  paginationEnabled?: boolean;
574
606
  pageSize?: number;
575
607
  checkFilterCombination?: (filterValues: FilterValues<any>,
@@ -9,6 +9,7 @@ import { FireCMSContext } from "./firecms_context";
9
9
  export interface FetchEntityProps<M extends Record<string, any> = any> {
10
10
  path: string;
11
11
  entityId: string;
12
+ databaseId?: string;
12
13
  collection?: EntityCollection<M, any>
13
14
  }
14
15
 
@@ -18,7 +19,6 @@ export interface FetchEntityProps<M extends Record<string, any> = any> {
18
19
  export type ListenEntityProps<M extends Record<string, any> = any> =
19
20
  FetchEntityProps<M>
20
21
  & {
21
- databaseId?: string;
22
22
  onUpdate: (entity: Entity<M>) => void,
23
23
  onError?: (error: Error) => void,
24
24
  }
@@ -139,6 +139,7 @@ export interface DataSource {
139
139
  fetchEntity<M extends Record<string, any> = any>({
140
140
  path,
141
141
  entityId,
142
+ databaseId,
142
143
  collection
143
144
  }: FetchEntityProps<M>
144
145
  ): Promise<Entity<M> | undefined>;