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

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 (173) hide show
  1. package/dist/components/ArrayContainer.d.ts +7 -3
  2. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
  3. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  4. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +15 -1
  5. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  6. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
  7. package/dist/components/PropertyIdCopyTooltip.d.ts +1 -1
  8. package/dist/components/SelectableTable/SelectableTable.d.ts +12 -2
  9. package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +1 -1
  10. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  11. package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -0
  12. package/dist/components/common/default_entity_actions.d.ts +0 -2
  13. package/dist/components/common/index.d.ts +1 -1
  14. package/dist/components/common/useColumnsIds.d.ts +1 -0
  15. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +10 -2
  16. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  17. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  18. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  19. package/dist/core/EntityEditView.d.ts +29 -21
  20. package/dist/core/EntityEditViewFormActions.d.ts +2 -0
  21. package/dist/core/FireCMSRouter.d.ts +4 -0
  22. package/dist/core/NavigationRoutes.d.ts +0 -1
  23. package/dist/core/SideDialogs.d.ts +4 -2
  24. package/dist/core/index.d.ts +2 -1
  25. package/dist/form/EntityForm.d.ts +45 -0
  26. package/dist/form/EntityFormActions.d.ts +17 -0
  27. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  28. package/dist/form/components/FormEntry.d.ts +6 -0
  29. package/dist/form/components/FormLayout.d.ts +5 -0
  30. package/dist/form/components/index.d.ts +2 -0
  31. package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
  32. package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
  33. package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
  34. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  35. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  36. package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
  37. package/dist/form/index.d.ts +16 -16
  38. package/dist/hooks/data/useEntityFetch.d.ts +2 -1
  39. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  40. package/dist/hooks/useBuildNavigationController.d.ts +4 -1
  41. package/dist/hooks/useModeController.d.ts +1 -2
  42. package/dist/index.es.js +18213 -16819
  43. package/dist/index.es.js.map +1 -1
  44. package/dist/index.umd.js +18164 -16771
  45. package/dist/index.umd.js.map +1 -1
  46. package/dist/internal/useBuildSideEntityController.d.ts +2 -2
  47. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  48. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  49. package/dist/routes/FireCMSRoute.d.ts +1 -0
  50. package/dist/routes/HomePageRoute.d.ts +3 -0
  51. package/dist/types/collections.d.ts +30 -10
  52. package/dist/types/datasource.d.ts +2 -2
  53. package/dist/types/dialogs_controller.d.ts +7 -3
  54. package/dist/types/entities.d.ts +1 -1
  55. package/dist/types/entity_actions.d.ts +10 -0
  56. package/dist/types/fields.d.ts +9 -2
  57. package/dist/types/navigation.d.ts +29 -12
  58. package/dist/types/plugins.d.ts +1 -0
  59. package/dist/types/properties.d.ts +18 -3
  60. package/dist/types/side_entity_controller.d.ts +6 -1
  61. package/dist/util/builders.d.ts +1 -1
  62. package/dist/util/entity_actions.d.ts +2 -0
  63. package/dist/util/entity_cache.d.ts +23 -0
  64. package/dist/util/index.d.ts +1 -0
  65. package/dist/util/navigation_from_path.d.ts +6 -1
  66. package/dist/util/navigation_utils.d.ts +12 -1
  67. package/dist/util/resolutions.d.ts +6 -1
  68. package/package.json +19 -21
  69. package/src/components/ArrayContainer.tsx +48 -29
  70. package/src/components/CircularProgressCenter.tsx +1 -1
  71. package/src/components/DeleteEntityDialog.tsx +1 -1
  72. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +40 -19
  73. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -4
  74. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +18 -1
  75. package/src/components/EntityCollectionTable/index.tsx +1 -1
  76. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
  77. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +5 -11
  78. package/src/components/EntityCollectionView/EntityCollectionView.tsx +83 -41
  79. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -2
  80. package/src/components/NotFoundPage.tsx +2 -2
  81. package/src/components/PropertyIdCopyTooltip.tsx +2 -3
  82. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +11 -6
  83. package/src/components/SelectableTable/SelectableTable.tsx +18 -3
  84. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +7 -7
  85. package/src/components/UnsavedChangesDialog.tsx +42 -0
  86. package/src/components/VirtualTable/VirtualTable.tsx +19 -2
  87. package/src/components/VirtualTable/VirtualTableProps.tsx +15 -0
  88. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +1 -1
  89. package/src/components/common/default_entity_actions.tsx +26 -37
  90. package/src/components/common/index.ts +1 -1
  91. package/src/components/common/useColumnsIds.tsx +1 -1
  92. package/src/components/common/useDataSourceTableController.tsx +420 -0
  93. package/src/components/common/useDebouncedCallback.tsx +20 -0
  94. package/src/components/common/useScrollRestoration.tsx +68 -0
  95. package/src/contexts/BreacrumbsContext.tsx +38 -0
  96. package/src/contexts/DialogsProvider.tsx +3 -2
  97. package/src/contexts/ModeController.tsx +1 -3
  98. package/src/core/DefaultAppBar.tsx +44 -14
  99. package/src/core/DefaultDrawer.tsx +1 -1
  100. package/src/core/EntityEditView.tsx +278 -1000
  101. package/src/core/EntityEditViewFormActions.tsx +199 -0
  102. package/src/core/EntitySidePanel.tsx +85 -15
  103. package/src/core/FireCMS.tsx +7 -4
  104. package/src/core/FireCMSRouter.tsx +17 -0
  105. package/src/core/NavigationRoutes.tsx +23 -32
  106. package/src/core/SideDialogs.tsx +20 -11
  107. package/src/core/index.tsx +4 -2
  108. package/src/form/EntityForm.tsx +774 -0
  109. package/src/form/EntityFormActions.tsx +169 -0
  110. package/src/form/PropertyFieldBinding.tsx +13 -11
  111. package/src/form/components/FormEntry.tsx +22 -0
  112. package/src/form/components/FormLayout.tsx +16 -0
  113. package/src/form/components/LabelWithIcon.tsx +6 -3
  114. package/src/form/components/StorageUploadProgress.tsx +1 -1
  115. package/src/form/components/index.tsx +2 -0
  116. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +4 -4
  117. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -2
  118. package/src/form/field_bindings/BlockFieldBinding.tsx +4 -2
  119. package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -0
  120. package/src/form/field_bindings/MapFieldBinding.tsx +14 -16
  121. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +9 -6
  122. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
  123. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -2
  124. package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -1
  125. package/src/form/field_bindings/RepeatFieldBinding.tsx +5 -2
  126. package/src/form/field_bindings/SelectFieldBinding.tsx +1 -1
  127. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +6 -4
  128. package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -0
  129. package/src/form/field_bindings/TextFieldBinding.tsx +1 -0
  130. package/src/form/index.tsx +16 -32
  131. package/src/form/validation.ts +12 -6
  132. package/src/hooks/data/save.ts +1 -1
  133. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  134. package/src/hooks/data/useEntityFetch.tsx +5 -1
  135. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  136. package/src/hooks/useBuildModeController.tsx +15 -28
  137. package/src/hooks/useBuildNavigationController.tsx +70 -24
  138. package/src/hooks/useLargeLayout.tsx +0 -35
  139. package/src/hooks/useModeController.tsx +1 -2
  140. package/src/hooks/useResolvedNavigationFrom.tsx +4 -6
  141. package/src/internal/useBuildSideDialogsController.tsx +3 -2
  142. package/src/internal/useBuildSideEntityController.tsx +124 -83
  143. package/src/internal/useUnsavedChangesDialog.tsx +126 -92
  144. package/src/preview/PropertyPreview.tsx +8 -8
  145. package/src/preview/components/UrlComponentPreview.tsx +17 -18
  146. package/src/preview/property_previews/MapPropertyPreview.tsx +3 -2
  147. package/src/routes/CustomCMSRoute.tsx +21 -0
  148. package/src/routes/FireCMSRoute.tsx +235 -0
  149. package/src/routes/HomePageRoute.tsx +17 -0
  150. package/src/types/collections.ts +38 -13
  151. package/src/types/datasource.ts +2 -1
  152. package/src/types/dialogs_controller.tsx +7 -3
  153. package/src/types/entities.ts +1 -1
  154. package/src/types/entity_actions.tsx +12 -0
  155. package/src/types/fields.tsx +13 -2
  156. package/src/types/navigation.ts +36 -16
  157. package/src/types/plugins.tsx +1 -0
  158. package/src/types/properties.ts +21 -3
  159. package/src/types/side_dialogs_controller.tsx +2 -0
  160. package/src/types/side_entity_controller.tsx +6 -1
  161. package/src/util/builders.ts +5 -3
  162. package/src/util/entity_actions.ts +28 -0
  163. package/src/util/entity_cache.ts +204 -0
  164. package/src/util/index.ts +1 -0
  165. package/src/util/join_collections.ts +3 -0
  166. package/src/util/navigation_from_path.ts +13 -6
  167. package/src/util/navigation_utils.ts +53 -1
  168. package/src/util/objects.ts +4 -1
  169. package/src/util/parent_references_from_path.ts +3 -3
  170. package/src/util/resolutions.ts +26 -5
  171. package/src/util/useStorageUploadController.tsx +39 -21
  172. package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -236
  173. /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
 
@@ -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
@@ -147,10 +148,12 @@ export function FireCMS<USER extends User, EC extends EntityCollection>(props: F
147
148
  <NavigationContext.Provider
148
149
  value={navigationController}>
149
150
  <DialogsProvider>
150
- <FireCMSInternal
151
- loading={loading}>
152
- {children}
153
- </FireCMSInternal>
151
+ <BreadcrumbsProvider>
152
+ <FireCMSInternal
153
+ loading={loading}>
154
+ {children}
155
+ </FireCMSInternal>
156
+ </BreadcrumbsProvider>
154
157
  </DialogsProvider>
155
158
  </NavigationContext.Provider>
156
159
  </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";