@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
@@ -0,0 +1,199 @@
1
+ import React, { useMemo } from "react";
2
+ import { Entity, EntityAction, FireCMSContext, ResolvedEntityCollection, SideEntityController } from "../types";
3
+
4
+ import { copyEntityAction, deleteEntityAction } from "../components";
5
+ import { canCreateEntity, canDeleteEntity, mergeEntityActions } from "../util";
6
+ import { Button, cls, defaultBorderMixin, DialogActions, IconButton, LoadingButton, Typography } from "@firecms/ui";
7
+ import { useAuthController, useFireCMSContext, useSideEntityController } from "../hooks";
8
+ import { EntityFormActionsProps } from "../form/EntityFormActions";
9
+ import { SideDialogController, useSideDialogContext } from "./SideDialogs";
10
+
11
+ export function EntityEditViewFormActions({
12
+ collection,
13
+ path,
14
+ entity,
15
+ layout,
16
+ savingError,
17
+ formex,
18
+ disabled,
19
+ status,
20
+ pluginActions,
21
+ openEntityMode,
22
+ showDefaultActions = true
23
+ }: EntityFormActionsProps) {
24
+
25
+ const authController = useAuthController();
26
+ const context = useFireCMSContext();
27
+ const sideEntityController = useSideEntityController();
28
+ const sideDialogContext = useSideDialogContext();
29
+
30
+ const entityActions = useMemo((): EntityAction[] => {
31
+ const customEntityActions = collection.entityActions
32
+ const createEnabled = canCreateEntity(collection, authController, path, null);
33
+ const deleteEnabled = entity ? canDeleteEntity(collection, authController, path, entity) : false;
34
+ const actions: EntityAction[] = [];
35
+ if (createEnabled)
36
+ actions.push(copyEntityAction);
37
+ if (deleteEnabled)
38
+ actions.push(deleteEntityAction);
39
+ if (customEntityActions)
40
+ return mergeEntityActions(actions, customEntityActions);
41
+ return actions;
42
+ }, [authController, collection, path]);
43
+
44
+ const formActions = showDefaultActions ? entityActions.filter(a => a.includeInForm === undefined || a.includeInForm) : [];
45
+
46
+ return layout === "bottom"
47
+ ? buildBottomActions({
48
+ savingError,
49
+ entity,
50
+ formActions,
51
+ collection,
52
+ context,
53
+ sideEntityController,
54
+ isSubmitting: formex.isSubmitting,
55
+ disabled,
56
+ status,
57
+ sideDialogContext,
58
+ pluginActions,
59
+ openEntityMode,
60
+ })
61
+ : buildSideActions({
62
+ savingError,
63
+ entity,
64
+ formActions,
65
+ collection,
66
+ context,
67
+ sideEntityController,
68
+ isSubmitting: formex.isSubmitting,
69
+ sideDialogContext,
70
+ disabled,
71
+ status,
72
+ pluginActions,
73
+ openEntityMode,
74
+ });
75
+ }
76
+
77
+ type ActionsViewProps<M extends object> = {
78
+ savingError: Error | undefined,
79
+ entity: Entity<M> | undefined,
80
+ formActions: EntityAction[],
81
+ collection: ResolvedEntityCollection,
82
+ context: FireCMSContext,
83
+ sideEntityController: SideEntityController,
84
+ isSubmitting: boolean,
85
+ disabled: boolean,
86
+ status: "new" | "existing" | "copy",
87
+ sideDialogContext: SideDialogController,
88
+ pluginActions?: React.ReactNode[],
89
+ openEntityMode: "side_panel" | "full_screen";
90
+ };
91
+
92
+ function buildBottomActions<M extends object>({
93
+ savingError,
94
+ entity,
95
+ formActions,
96
+ collection,
97
+ context,
98
+ sideEntityController,
99
+ isSubmitting,
100
+ disabled,
101
+ status,
102
+ sideDialogContext,
103
+ pluginActions,
104
+ openEntityMode,
105
+ }: ActionsViewProps<M>) {
106
+
107
+ const canClose = openEntityMode === "side_panel";
108
+ return <DialogActions position={"absolute"}>
109
+ {savingError &&
110
+ <div className="text-right">
111
+ <Typography color={"error"}>{savingError.message}</Typography>
112
+ </div>
113
+ }
114
+ {entity && formActions.length > 0 && <div className="flex-grow flex overflow-auto no-scrollbar">
115
+ {formActions.map(action => (
116
+ <IconButton
117
+ key={action.name}
118
+ color="primary"
119
+ onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
120
+ event.stopPropagation();
121
+ if (entity)
122
+ action.onClick({
123
+ entity,
124
+ fullPath: collection.path,
125
+ collection: collection,
126
+ context,
127
+ sideEntityController,
128
+ openEntityMode: openEntityMode
129
+ });
130
+ }}>
131
+ {action.icon}
132
+ </IconButton>
133
+ ))}
134
+ </div>}
135
+ {pluginActions}
136
+ <Button variant="text" disabled={disabled || isSubmitting} type="reset">
137
+ {status === "existing" ? "Discard" : "Clear"}
138
+ </Button>
139
+ <Button variant={canClose ? "text" : "filled"} color="primary" type="submit"
140
+ disabled={disabled || isSubmitting}
141
+ onClick={() => {
142
+ sideDialogContext.setPendingClose(false);
143
+ }}>
144
+ {status === "existing" && "Save"}
145
+ {status === "copy" && "Create copy"}
146
+ {status === "new" && "Create"}
147
+ </Button>
148
+ {canClose && <LoadingButton variant="filled"
149
+ color="primary"
150
+ type="submit"
151
+ loading={isSubmitting}
152
+ disabled={disabled}
153
+ onClick={() => {
154
+ sideDialogContext.setPendingClose?.(true);
155
+ }}>
156
+ {status === "existing" && "Save and close"}
157
+ {status === "copy" && "Create copy and close"}
158
+ {status === "new" && "Create and close"}
159
+ </LoadingButton>}
160
+ </DialogActions>;
161
+ }
162
+
163
+ function buildSideActions<M extends object>({
164
+ savingError,
165
+ entity,
166
+ formActions,
167
+ collection,
168
+ context,
169
+ sideEntityController,
170
+ isSubmitting,
171
+ disabled,
172
+ status,
173
+ sideDialogContext,
174
+ pluginActions
175
+ }: ActionsViewProps<M>) {
176
+
177
+ return <div
178
+ className={cls("overflow-auto h-full flex flex-col gap-2 w-80 2xl:w-96 px-4 py-16 sticky top-0 border-l", defaultBorderMixin)}>
179
+ <LoadingButton fullWidth={true} variant="filled" color="primary" type="submit" size={"large"}
180
+ disabled={disabled || isSubmitting} onClick={() => {
181
+ sideDialogContext.setPendingClose?.(false);
182
+ }}>
183
+ {status === "existing" && "Save"}
184
+ {status === "copy" && "Create copy"}
185
+ {status === "new" && "Create"}
186
+ </LoadingButton>
187
+ <Button fullWidth={true} variant="text" disabled={disabled || isSubmitting} type="reset">
188
+ {status === "existing" ? "Discard" : "Clear"}
189
+ </Button>
190
+
191
+ {pluginActions}
192
+
193
+ {savingError &&
194
+ <div className="text-right">
195
+ <Typography color={"error"}>{savingError.message}</Typography>
196
+ </div>
197
+ }
198
+ </div>;
199
+ }
@@ -1,11 +1,13 @@
1
1
  import React, { useCallback, useEffect, useMemo } from "react";
2
2
 
3
3
  import { EntitySidePanelProps } from "../types";
4
- import { useNavigationController } from "../hooks";
4
+ import { useNavigationController, useSideEntityController } from "../hooks";
5
5
 
6
6
  import { ErrorBoundary } from "../components";
7
- import { EntityEditView } from "./EntityEditView";
7
+ import { EntityEditView, OnUpdateParams } from "./EntityEditView";
8
8
  import { useSideDialogContext } from "./SideDialogs";
9
+ import { CloseIcon, IconButton, OpenInFullIcon } from "@firecms/ui";
10
+ import { useLocation, useNavigate } from "react-router-dom";
9
11
 
10
12
  /**
11
13
  * This is the component in charge of rendering the side dialog used
@@ -20,30 +22,64 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
20
22
  const {
21
23
  blocked,
22
24
  setBlocked,
23
- setBlockedNavigationMessage
25
+ setBlockedNavigationMessage,
26
+ close
24
27
  } = useSideDialogContext();
25
28
 
29
+ const navigate = useNavigate();
30
+ const location = useLocation();
31
+
32
+ const sideEntityController = useSideEntityController();
26
33
  const navigationController = useNavigationController();
34
+ const sideDialogsController = useSideDialogContext();
35
+
36
+ const onClose = () => {
37
+ if (props.onClose) {
38
+ props.onClose();
39
+ }
40
+
41
+ setBlocked(false);
42
+ close(true);
43
+ }
44
+
45
+ const onUpdate = (params: OnUpdateParams) => {
46
+ if (props.onUpdate) {
47
+ props.onUpdate(params);
48
+ }
49
+ if (params.status !== "existing") {
50
+ sideEntityController.replace({
51
+ path: params.path,
52
+ entityId: params.entityId,
53
+ selectedTab: params.selectedTab,
54
+ updateUrl: true,
55
+ collection: params.collection,
56
+ });
57
+ }
58
+
59
+ if (sideDialogsController.pendingClose) {
60
+ sideDialogsController.setPendingClose(false);
61
+ onClose();
62
+ }
63
+
64
+ }
27
65
 
28
66
  const parentCollectionIds = useMemo(() => {
29
67
  return navigationController.getParentCollectionIds(props.path);
30
68
  }, [navigationController, props.path]);
31
69
 
32
70
  const collection = useMemo(() => {
33
- if (!props) return undefined;
34
- let usedCollection = props.collection;
71
+ if (props.collection) {
72
+ return props.collection;
73
+ }
35
74
 
36
75
  const registryCollection = navigationController.getCollection(props.path);
37
76
  if (registryCollection) {
38
- usedCollection = registryCollection;
39
- }
40
- if (!usedCollection) {
41
- console.error("ERROR: No collection found in path `", props.path, "`. Entity id: ", props.entityId);
42
- throw Error("ERROR: No collection found in path `" + props.path + "`. Make sure you have defined a collection for this path in the root navigation.");
77
+ return registryCollection;
43
78
  }
44
79
 
45
- return usedCollection;
46
- }, [navigationController, props]);
80
+ console.error("ERROR: No collection found in path `", props.path, "`. Entity id: ", props.entityId);
81
+ throw Error("ERROR: No collection found in path `" + props.path + "`. Make sure you have defined a collection for this path in the root navigation.");
82
+ }, [navigationController, props.collection]);
47
83
 
48
84
  useEffect(() => {
49
85
  function beforeunload(e: any) {
@@ -63,11 +99,11 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
63
99
 
64
100
  }, [blocked, collection]);
65
101
 
66
- const onValuesAreModified = useCallback((modified: boolean) => {
67
- setBlocked(modified);
102
+ const onValuesModified = useCallback((modified: boolean) => {
68
103
  setBlockedNavigationMessage(modified
69
104
  ? <> You have unsaved changes in this <b>{collection?.singularName ?? collection?.name}</b>.</>
70
105
  : undefined)
106
+ setBlocked(modified);
71
107
  }, [collection?.name, setBlocked, setBlockedNavigationMessage]);
72
108
 
73
109
  if (!props || !collection) {
@@ -79,9 +115,43 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
79
115
  <ErrorBoundary>
80
116
  <EntityEditView
81
117
  {...props}
118
+ layout={"side_panel"}
82
119
  collection={collection}
83
120
  parentCollectionIds={parentCollectionIds}
84
- onValuesAreModified={onValuesAreModified}
121
+ onValuesModified={onValuesModified}
122
+ onSaved={onUpdate}
123
+ barActions={<>
124
+ <IconButton
125
+ className="self-center"
126
+ onClick={onClose}>
127
+ <CloseIcon size={"small"}/>
128
+ </IconButton>
129
+ <IconButton
130
+ className="self-center"
131
+ onClick={() => {
132
+ if (props.entityId)
133
+ navigate(location.pathname);
134
+ else
135
+ navigate(location.pathname + "#new");
136
+ }}>
137
+ <OpenInFullIcon size={"small"}/>
138
+ </IconButton>
139
+ </>}
140
+ onTabChange={({
141
+ path,
142
+ entityId,
143
+ selectedTab,
144
+ collection,
145
+ }) => {
146
+ sideEntityController.replace({
147
+ path,
148
+ entityId,
149
+ selectedTab,
150
+ updateUrl: true,
151
+ collection,
152
+ });
153
+ }}
154
+ formProps={props.formProps}
85
155
  />
86
156
  </ErrorBoundary>
87
157
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { useMemo } from "react";
4
4
  import { CenteredView, Typography } from "@firecms/ui";
5
- import { CustomizationController, EntityCollection, FireCMSContext, FireCMSPlugin, FireCMSProps, User } from "../types";
5
+ import { CustomizationController, FireCMSContext, FireCMSPlugin, FireCMSProps, User } from "../types";
6
6
  import { AuthControllerContext } from "../contexts";
7
7
  import { useBuildSideEntityController } from "../internal/useBuildSideEntityController";
8
8
  import { useCustomizationController, useFireCMSContext } from "../hooks";
@@ -19,6 +19,7 @@ import { useBuildDataSource } from "../internal/useBuildDataSource";
19
19
  import { CustomizationControllerContext } from "../contexts/CustomizationControllerContext";
20
20
  import { AnalyticsContext } from "../contexts/AnalyticsContext";
21
21
  import { useProjectLog } from "../hooks/useProjectLog";
22
+ import { BreadcrumbsProvider } from "../contexts/BreacrumbsContext";
22
23
 
23
24
  /**
24
25
  * If you are using independent components of the CMS
@@ -32,7 +33,7 @@ import { useProjectLog } from "../hooks/useProjectLog";
32
33
 
33
34
  * @group Core
34
35
  */
35
- export function FireCMS<USER extends User, EC extends EntityCollection>(props: FireCMSProps<USER, EC>) {
36
+ export function FireCMS<USER extends User>(props: FireCMSProps<USER>) {
36
37
 
37
38
  const {
38
39
  children,
@@ -52,17 +53,8 @@ export function FireCMS<USER extends User, EC extends EntityCollection>(props: F
52
53
  apiKey
53
54
  } = props;
54
55
 
55
- /**
56
- * Controller in charge of fetching and persisting data
57
- */
58
- const dataSource = useBuildDataSource({
59
- delegate: dataSourceDelegate,
60
- propertyConfigs,
61
- navigationController
62
- });
63
-
64
56
  const sideDialogsController = useBuildSideDialogsController();
65
- const sideEntityController = useBuildSideEntityController(navigationController, sideDialogsController);
57
+ const sideEntityController = useBuildSideEntityController(navigationController, sideDialogsController, authController);
66
58
 
67
59
  const pluginsLoading = plugins?.some(p => p.loading) ?? false;
68
60
 
@@ -89,6 +81,16 @@ export function FireCMS<USER extends User, EC extends EntityCollection>(props: F
89
81
  plugins
90
82
  });
91
83
 
84
+ /**
85
+ * Controller in charge of fetching and persisting data
86
+ */
87
+ const dataSource = useBuildDataSource({
88
+ delegate: dataSourceDelegate,
89
+ propertyConfigs,
90
+ navigationController,
91
+ authController
92
+ });
93
+
92
94
  if (accessResponse?.message) {
93
95
  console.warn(accessResponse.message);
94
96
  }
@@ -147,10 +149,12 @@ export function FireCMS<USER extends User, EC extends EntityCollection>(props: F
147
149
  <NavigationContext.Provider
148
150
  value={navigationController}>
149
151
  <DialogsProvider>
150
- <FireCMSInternal
151
- loading={loading}>
152
- {children}
153
- </FireCMSInternal>
152
+ <BreadcrumbsProvider>
153
+ <FireCMSInternal
154
+ loading={loading}>
155
+ {children}
156
+ </FireCMSInternal>
157
+ </BreadcrumbsProvider>
154
158
  </DialogsProvider>
155
159
  </NavigationContext.Provider>
156
160
  </SideEntityControllerContext.Provider>
@@ -0,0 +1,17 @@
1
+ import { createBrowserRouter, RouterProvider } from "react-router-dom"
2
+ import { removeInitialAndTrailingSlashes } from "../util";
3
+
4
+ export function FireCMSRouter({
5
+ children,
6
+ basePath
7
+ }: {
8
+ children: React.ReactNode,
9
+ basePath?: string;
10
+ }) {
11
+ return <RouterProvider router={createBrowserRouter([
12
+ {
13
+ path: basePath ? `${removeInitialAndTrailingSlashes(basePath)}/*` : "/*",
14
+ element: children
15
+ }
16
+ ])}/>;
17
+ }
@@ -2,9 +2,11 @@ import React from "react";
2
2
 
3
3
  import { Route, Routes, useLocation } from "react-router-dom";
4
4
  import { CMSView } from "../types";
5
- import { DefaultHomePage, EntityCollectionView, ErrorBoundary, NotFoundPage } from "../components";
5
+ import { DefaultHomePage, ErrorBoundary, NotFoundPage } from "../components";
6
6
  import { useNavigationController } from "../hooks";
7
- import { toArray } from "../util/arrays";
7
+ import { FireCMSRoute } from "../routes/FireCMSRoute";
8
+ import { CustomCMSRoute } from "../routes/CustomCMSRoute";
9
+ import { HomePageRoute } from "../routes/HomePageRoute";
8
10
 
9
11
  /**
10
12
  * @group Components
@@ -25,7 +27,6 @@ export type NavigationRoutesProps = {
25
27
  * or the home route) related to a {@link NavigationController}.
26
28
  * This component needs a parent {@link FireCMS}
27
29
  *
28
-
29
30
  * @group Components
30
31
  */
31
32
  export const NavigationRoutes = React.memo<NavigationRoutesProps>(
@@ -67,43 +68,33 @@ export const NavigationRoutes = React.memo<NavigationRoutesProps>(
67
68
  });
68
69
  }
69
70
 
70
- // we reorder collections so that nested paths are included first
71
- const sortedCollections = [...(navigation.collections ?? [])]
72
- .sort((a, b) => b.path.length - a.path.length);
73
-
74
- const collectionRoutes = sortedCollections
75
- .map((collection) => {
76
- const urlPath = navigation.buildUrlCollectionPath(collection.id ?? collection.path);
77
- return <Route path={urlPath + "/*"}
78
- key={`navigation_${collection.id ?? collection.path}`}
79
- element={
80
- <ErrorBoundary>
81
- <EntityCollectionView
82
- key={`collection_view_${collection.id ?? collection.path}`}
83
- isSubCollection={false}
84
- parentCollectionIds={[]}
85
- fullPath={collection.id ?? collection.path}
86
- {...collection}
87
- Actions={toArray(collection.Actions)}/>
88
- </ErrorBoundary>
89
- }/>;
90
- }
91
- );
71
+ const collectionUrlPath = navigation.buildUrlCollectionPath("");
72
+ const collectionRoute = (
73
+ <Route path={collectionUrlPath + "/*"}
74
+ key={`navigation_entity`}
75
+ element={
76
+ <ErrorBoundary>
77
+ <FireCMSRoute/>
78
+ </ErrorBoundary>
79
+ }/>
80
+ );
92
81
 
93
82
  const homeRoute = (
94
83
  <Route path={"/"}
95
- element={homePage}/>
84
+ element={<HomePageRoute>{homePage}</HomePageRoute>}/>
96
85
  );
97
86
 
98
- const notFoundRoute = <Route path={"*"}
99
- element={
100
- <NotFoundPage/>
101
- }/>;
87
+ const notFoundRoute = (
88
+ <Route path={"*"}
89
+ element={
90
+ <NotFoundPage/>
91
+ }/>
92
+ );
102
93
 
103
94
  return (
104
95
  <Routes location={baseLocation}>
105
96
 
106
- {collectionRoutes}
97
+ {collectionRoute}
107
98
 
108
99
  {cmsViews}
109
100
 
@@ -121,6 +112,6 @@ const buildCMSViewRoute = (path: string, cmsView: CMSView) => {
121
112
  return <Route
122
113
  key={"navigation_view_" + path}
123
114
  path={path}
124
- element={cmsView.view}
115
+ element={<CustomCMSRoute cmsView={cmsView}/>}
125
116
  />;
126
117
  };
@@ -1,19 +1,22 @@
1
- import React, { useContext, useEffect, useState } from "react";
1
+ import React, { useCallback, useContext, useEffect, useRef, useState } from "react";
2
2
  import { useSideDialogsController } from "../hooks";
3
3
  import { SideDialogPanelProps } from "../types";
4
4
  import { Sheet } from "@firecms/ui";
5
- import { UnsavedChangesDialog, useNavigationUnsavedChangesDialog } from "../internal/useUnsavedChangesDialog";
5
+ import { useNavigationUnsavedChangesDialog } from "../internal/useUnsavedChangesDialog";
6
6
  import { ErrorBoundary } from "../components";
7
+ import { UnsavedChangesDialog } from "../components/UnsavedChangesDialog";
7
8
 
8
- export type SideDialogContextProps = {
9
+ export type SideDialogController = {
9
10
  blocked: boolean,
10
11
  setBlocked: (blocked: boolean) => void,
11
12
  setBlockedNavigationMessage: (message?: React.ReactNode) => void,
12
13
  width?: string,
13
- close: (force?: boolean) => void
14
+ close: (force?: boolean) => void;
15
+ pendingClose: boolean,
16
+ setPendingClose: (pendingClose: boolean) => void
14
17
  }
15
18
 
16
- const SideDialogContext = React.createContext<SideDialogContextProps>({
19
+ const SideDialogContext = React.createContext<SideDialogController>({
17
20
  width: "",
18
21
  blocked: false,
19
22
  setBlocked: (blocked: boolean) => {
@@ -21,6 +24,10 @@ const SideDialogContext = React.createContext<SideDialogContextProps>({
21
24
  setBlockedNavigationMessage: (message?: React.ReactNode) => {
22
25
  },
23
26
  close: () => {
27
+ },
28
+ pendingClose: false,
29
+ setPendingClose: () => {
30
+
24
31
  }
25
32
  });
26
33
 
@@ -29,7 +36,7 @@ const SideDialogContext = React.createContext<SideDialogContextProps>({
29
36
  * in contrast with {@link useSideDialogsController} which handles the
30
37
  * state of all the dialogs.
31
38
  */
32
- export const useSideDialogContext = () => useContext<SideDialogContextProps>(SideDialogContext);
39
+ export const useSideDialogContext = () => useContext<SideDialogController>(SideDialogContext);
33
40
 
34
41
  /**
35
42
  * This is the component in charge of rendering the side dialogs used
@@ -72,6 +79,8 @@ function SideDialogView({
72
79
  const [blocked, setBlocked] = useState(false);
73
80
  const [blockedNavigationMessage, setBlockedNavigationMessage] = useState<React.ReactNode | undefined>();
74
81
 
82
+ const [pendingClose, setPendingClose] = useState(false);
83
+
75
84
  const widthRef = React.useRef<string | undefined>(panel?.width);
76
85
  const width = widthRef.current;
77
86
 
@@ -111,8 +120,6 @@ function SideDialogView({
111
120
  }
112
121
  };
113
122
 
114
- // const offsetClass = offsetPosition > 0 ? `transform translate-x-${offsetPosition}` : "";
115
-
116
123
  return (
117
124
  <SideDialogContext.Provider
118
125
  value={{
@@ -120,7 +127,9 @@ function SideDialogView({
120
127
  setBlocked,
121
128
  setBlockedNavigationMessage,
122
129
  width,
123
- close: onCloseRequest
130
+ close: onCloseRequest,
131
+ pendingClose,
132
+ setPendingClose
124
133
  }}>
125
134
 
126
135
  <Sheet
@@ -133,7 +142,7 @@ function SideDialogView({
133
142
  className={"transform max-w-[100vw] lg:max-w-[95vw] flex flex-col h-full transition-all duration-250 ease-in-out bg-white dark:bg-surface-900 "}
134
143
  style={{
135
144
  width: panel.width,
136
- transform: `translateX(-${offsetPosition * 200}px)`
145
+ transform: `translateX(-${offsetPosition * 200}px)`,
137
146
  }}
138
147
  >
139
148
  <ErrorBoundary>
@@ -146,7 +155,7 @@ function SideDialogView({
146
155
  </Sheet>
147
156
 
148
157
  <UnsavedChangesDialog
149
- open={navigationWasBlocked || drawerCloseRequested}
158
+ open={drawerCloseRequested}
150
159
  handleOk={drawerCloseRequested ? handleDrawerCloseOk : handleNavigationOk}
151
160
  handleCancel={drawerCloseRequested ? handleDrawerCloseCancel : handleNavigationCancel}
152
161
  body={blockedNavigationMessage}/>
@@ -1,7 +1,5 @@
1
1
  export * from "./FireCMS";
2
2
 
3
- export * from "../app/Scaffold";
4
-
5
3
  export * from "./DefaultDrawer";
6
4
  export * from "./DrawerNavigationItem";
7
5
 
@@ -9,3 +7,7 @@ export * from "./field_configs";
9
7
 
10
8
  export * from "./SideDialogs";
11
9
  export * from "./NavigationRoutes";
10
+
11
+ export * from "./FireCMSRouter";
12
+
13
+ export * from "../app";