@firecms/core 3.0.0-canary.168 → 3.0.0-canary.169

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 (96) hide show
  1. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
  2. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  3. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +15 -1
  4. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  5. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
  6. package/dist/components/SelectableTable/SelectableTable.d.ts +12 -2
  7. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  8. package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -0
  9. package/dist/components/common/index.d.ts +1 -1
  10. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +10 -2
  11. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  12. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  13. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  14. package/dist/core/EntityEditView.d.ts +25 -7
  15. package/dist/core/FireCMSRouter.d.ts +4 -0
  16. package/dist/core/NavigationRoutes.d.ts +0 -1
  17. package/dist/core/index.d.ts +2 -1
  18. package/dist/hooks/data/useEntityFetch.d.ts +2 -1
  19. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  20. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  21. package/dist/index.es.js +2344 -1305
  22. package/dist/index.es.js.map +1 -1
  23. package/dist/index.umd.js +2344 -1306
  24. package/dist/index.umd.js.map +1 -1
  25. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  26. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  27. package/dist/routes/FireCMSRoute.d.ts +1 -0
  28. package/dist/routes/HomePageRoute.d.ts +3 -0
  29. package/dist/types/collections.d.ts +18 -6
  30. package/dist/types/datasource.d.ts +2 -2
  31. package/dist/types/dialogs_controller.d.ts +3 -7
  32. package/dist/types/entities.d.ts +1 -1
  33. package/dist/types/entity_actions.d.ts +1 -6
  34. package/dist/types/navigation.d.ts +28 -12
  35. package/dist/types/plugins.d.ts +1 -0
  36. package/dist/types/side_entity_controller.d.ts +1 -1
  37. package/dist/util/entity_cache.d.ts +23 -0
  38. package/dist/util/navigation_from_path.d.ts +5 -1
  39. package/dist/util/navigation_utils.d.ts +12 -1
  40. package/package.json +5 -5
  41. package/src/components/CircularProgressCenter.tsx +1 -1
  42. package/src/components/DeleteEntityDialog.tsx +1 -1
  43. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +40 -19
  44. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -4
  45. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +18 -1
  46. package/src/components/EntityCollectionTable/index.tsx +1 -1
  47. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
  48. package/src/components/EntityCollectionView/EntityCollectionView.tsx +79 -38
  49. package/src/components/NotFoundPage.tsx +2 -2
  50. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -5
  51. package/src/components/SelectableTable/SelectableTable.tsx +18 -3
  52. package/src/components/UnsavedChangesDialog.tsx +42 -0
  53. package/src/components/VirtualTable/VirtualTable.tsx +19 -2
  54. package/src/components/VirtualTable/VirtualTableProps.tsx +15 -0
  55. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +1 -1
  56. package/src/components/common/default_entity_actions.tsx +24 -10
  57. package/src/components/common/index.ts +1 -1
  58. package/src/components/common/{useDataSourceEntityCollectionTableController.tsx → useDataSourceTableController.tsx} +122 -13
  59. package/src/components/common/useDebouncedCallback.tsx +20 -0
  60. package/src/components/common/useScrollRestoration.tsx +68 -0
  61. package/src/contexts/BreacrumbsContext.tsx +38 -0
  62. package/src/contexts/DialogsProvider.tsx +2 -3
  63. package/src/core/DefaultAppBar.tsx +28 -4
  64. package/src/core/EntityEditView.tsx +457 -296
  65. package/src/core/EntitySidePanel.tsx +72 -6
  66. package/src/core/FireCMS.tsx +7 -4
  67. package/src/core/FireCMSRouter.tsx +17 -0
  68. package/src/core/NavigationRoutes.tsx +15 -28
  69. package/src/core/SideDialogs.tsx +10 -5
  70. package/src/core/index.tsx +4 -2
  71. package/src/hooks/data/save.ts +1 -1
  72. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  73. package/src/hooks/data/useEntityFetch.tsx +5 -1
  74. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  75. package/src/hooks/useBuildNavigationController.tsx +59 -21
  76. package/src/hooks/useLargeLayout.tsx +0 -35
  77. package/src/hooks/useResolvedNavigationFrom.tsx +4 -6
  78. package/src/internal/useBuildSideDialogsController.tsx +4 -2
  79. package/src/internal/useBuildSideEntityController.tsx +157 -77
  80. package/src/internal/useUnsavedChangesDialog.tsx +126 -92
  81. package/src/routes/CustomCMSRoute.tsx +21 -0
  82. package/src/routes/FireCMSRoute.tsx +230 -0
  83. package/src/routes/HomePageRoute.tsx +17 -0
  84. package/src/types/collections.ts +19 -6
  85. package/src/types/datasource.ts +2 -1
  86. package/src/types/dialogs_controller.tsx +3 -7
  87. package/src/types/entities.ts +1 -1
  88. package/src/types/entity_actions.tsx +1 -7
  89. package/src/types/navigation.ts +35 -16
  90. package/src/types/plugins.tsx +1 -0
  91. package/src/types/side_entity_controller.tsx +1 -1
  92. package/src/util/entity_cache.ts +196 -0
  93. package/src/util/navigation_from_path.ts +10 -2
  94. package/src/util/navigation_utils.ts +53 -1
  95. package/src/util/parent_references_from_path.ts +3 -3
  96. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -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,11 +22,41 @@ 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();
27
34
 
35
+ const onClose = () => {
36
+ if (props.onClose) {
37
+ props.onClose();
38
+ }
39
+
40
+ setBlocked(false);
41
+ close(true);
42
+ }
43
+
44
+ const onUpdate = (params: OnUpdateParams) => {
45
+ if (props.onUpdate) {
46
+ props.onUpdate(params);
47
+ }
48
+ if (params.status !== "existing") {
49
+ sideEntityController.replace({
50
+ path: params.path,
51
+ entityId: params.entityId,
52
+ selectedTab: params.selectedTab,
53
+ updateUrl: true,
54
+ collection: params.collection,
55
+ });
56
+ }
57
+
58
+ }
59
+
28
60
  const parentCollectionIds = useMemo(() => {
29
61
  return navigationController.getParentCollectionIds(props.path);
30
62
  }, [navigationController, props.path]);
@@ -63,11 +95,11 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
63
95
 
64
96
  }, [blocked, collection]);
65
97
 
66
- const onValuesAreModified = useCallback((modified: boolean) => {
67
- setBlocked(modified);
98
+ const onValuesModified = useCallback((modified: boolean) => {
68
99
  setBlockedNavigationMessage(modified
69
100
  ? <> You have unsaved changes in this <b>{collection?.singularName ?? collection?.name}</b>.</>
70
101
  : undefined)
102
+ setBlocked(modified);
71
103
  }, [collection?.name, setBlocked, setBlockedNavigationMessage]);
72
104
 
73
105
  if (!props || !collection) {
@@ -79,9 +111,43 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
79
111
  <ErrorBoundary>
80
112
  <EntityEditView
81
113
  {...props}
114
+ layout={"side_panel"}
82
115
  collection={collection}
83
116
  parentCollectionIds={parentCollectionIds}
84
- onValuesAreModified={onValuesAreModified}
117
+ onValuesModified={onValuesModified}
118
+ onClose={onClose}
119
+ onSaved={onUpdate}
120
+ barActions={<>
121
+ <IconButton
122
+ className="self-center"
123
+ onClick={onClose}>
124
+ <CloseIcon size={"small"}/>
125
+ </IconButton>
126
+ <IconButton
127
+ className="self-center"
128
+ onClick={() => {
129
+ if (props.entityId)
130
+ navigate(location.pathname);
131
+ else
132
+ navigate(location.pathname + "#new");
133
+ }}>
134
+ <OpenInFullIcon size={"small"}/>
135
+ </IconButton>
136
+ </>}
137
+ onTabChange={({
138
+ path,
139
+ entityId,
140
+ selectedTab,
141
+ collection
142
+ }) => {
143
+ sideEntityController.replace({
144
+ path,
145
+ entityId,
146
+ selectedTab,
147
+ updateUrl: true,
148
+ collection
149
+ });
150
+ }}
85
151
  />
86
152
  </ErrorBoundary>
87
153
 
@@ -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,32 +68,18 @@ 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 urlPath = navigation.buildUrlCollectionPath("");
72
+ const collectionRoute = <Route path={urlPath + "/*"}
73
+ key={`navigation_entity`}
74
+ element={
75
+ <ErrorBoundary>
76
+ <FireCMSRoute/>
77
+ </ErrorBoundary>
78
+ }/>
92
79
 
93
80
  const homeRoute = (
94
81
  <Route path={"/"}
95
- element={homePage}/>
82
+ element={<HomePageRoute>{homePage}</HomePageRoute>}/>
96
83
  );
97
84
 
98
85
  const notFoundRoute = <Route path={"*"}
@@ -103,7 +90,7 @@ export const NavigationRoutes = React.memo<NavigationRoutesProps>(
103
90
  return (
104
91
  <Routes location={baseLocation}>
105
92
 
106
- {collectionRoutes}
93
+ {collectionRoute}
107
94
 
108
95
  {cmsViews}
109
96
 
@@ -121,6 +108,6 @@ const buildCMSViewRoute = (path: string, cmsView: CMSView) => {
121
108
  return <Route
122
109
  key={"navigation_view_" + path}
123
110
  path={path}
124
- element={cmsView.view}
111
+ element={<CustomCMSRoute cmsView={cmsView}/>}
125
112
  />;
126
113
  };
@@ -2,8 +2,9 @@ import React, { useContext, useEffect, 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
9
  export type SideDialogContextProps = {
9
10
  blocked: boolean,
@@ -111,8 +112,6 @@ function SideDialogView({
111
112
  }
112
113
  };
113
114
 
114
- // const offsetClass = offsetPosition > 0 ? `transform translate-x-${offsetPosition}` : "";
115
-
116
115
  return (
117
116
  <SideDialogContext.Provider
118
117
  value={{
@@ -127,13 +126,19 @@ function SideDialogView({
127
126
  open={Boolean(panel)}
128
127
  onOpenChange={(open) => !open && onCloseRequest()}
129
128
  title={"Side dialog " + panel?.key}
129
+ // style={{
130
+ // zIndex: offsetPosition * 20 + 20,
131
+ // }}
132
+ // overlayStyle={{
133
+ // zIndex: offsetPosition * 20 + 10,
134
+ // }}
130
135
  >
131
136
  {panel &&
132
137
  <div
133
138
  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
139
  style={{
135
140
  width: panel.width,
136
- transform: `translateX(-${offsetPosition * 200}px)`
141
+ transform: `translateX(-${offsetPosition * 200}px)`,
137
142
  }}
138
143
  >
139
144
  <ErrorBoundary>
@@ -146,7 +151,7 @@ function SideDialogView({
146
151
  </Sheet>
147
152
 
148
153
  <UnsavedChangesDialog
149
- open={navigationWasBlocked || drawerCloseRequested}
154
+ open={drawerCloseRequested}
150
155
  handleOk={drawerCloseRequested ? handleDrawerCloseOk : handleNavigationOk}
151
156
  handleCancel={drawerCloseRequested ? handleDrawerCloseCancel : handleNavigationCancel}
152
157
  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";
@@ -75,7 +75,7 @@ export async function saveEntityWithCallbacks<M extends Record<string, any>, USE
75
75
 
76
76
  const customizationController = context.customizationController;
77
77
 
78
- const resolvedPath = context.navigation.resolveAliasesFrom(path);
78
+ const resolvedPath = context.navigation.resolveIdsFrom(path);
79
79
 
80
80
  const callbacks = collection.callbacks;
81
81
  if (callbacks?.onPreSave) {
@@ -73,7 +73,7 @@ export function useCollectionFetch<M extends Record<string, any>, USER extends U
73
73
  const dataSource = useDataSource(collection);
74
74
  const navigationController = useNavigationController();
75
75
 
76
- const path = navigationController.resolveAliasesFrom(inputPath);
76
+ const path = navigationController.resolveIdsFrom(inputPath);
77
77
 
78
78
  const sortByProperty = sortBy ? sortBy[0] : undefined;
79
79
  const currentSort = sortBy ? sortBy[1] : undefined;
@@ -10,6 +10,7 @@ import { useFireCMSContext } from "../useFireCMSContext";
10
10
  export interface EntityFetchProps<M extends Record<string, any>, USER extends User = User> {
11
11
  path: string;
12
12
  entityId?: string;
13
+ databaseId?: string;
13
14
  collection: EntityCollection<M, USER>;
14
15
  useCache?: boolean;
15
16
  }
@@ -40,13 +41,14 @@ export function useEntityFetch<M extends Record<string, any>, USER extends User>
40
41
  path: inputPath,
41
42
  entityId,
42
43
  collection,
44
+ databaseId,
43
45
  useCache = false
44
46
  }: EntityFetchProps<M, USER>): EntityFetchResult<M> {
45
47
 
46
48
  const dataSource = useDataSource(collection);
47
49
  const navigationController = useNavigationController();
48
50
 
49
- const path = navigationController.resolveAliasesFrom(inputPath);
51
+ const path = navigationController.resolveIdsFrom(inputPath);
50
52
 
51
53
  const context: FireCMSContext<USER> = useFireCMSContext();
52
54
 
@@ -96,6 +98,7 @@ export function useEntityFetch<M extends Record<string, any>, USER extends User>
96
98
  return dataSource.listenEntity<M>({
97
99
  path,
98
100
  entityId,
101
+ databaseId,
99
102
  collection,
100
103
  onUpdate: onEntityUpdate,
101
104
  onError
@@ -104,6 +107,7 @@ export function useEntityFetch<M extends Record<string, any>, USER extends User>
104
107
  dataSource.fetchEntity<M>({
105
108
  path,
106
109
  entityId,
110
+ databaseId,
107
111
  collection
108
112
  })
109
113
  .then(onEntityUpdate)
@@ -0,0 +1,31 @@
1
+ import { useContext } from "react";
2
+ import { BreadcrumbContext } from "../contexts/BreacrumbsContext";
3
+
4
+ /**
5
+ * @group Hooks and utilities
6
+ */
7
+ export interface BreadcrumbsController {
8
+ breadcrumbs: BreadcrumbEntry[];
9
+ set: (props: {
10
+ breadcrumbs: BreadcrumbEntry[];
11
+ }) => void;
12
+ }
13
+
14
+ /**
15
+ * @group Hooks and utilities
16
+ */
17
+ export interface BreadcrumbEntry {
18
+ title: string;
19
+ url: string;
20
+ }
21
+
22
+ /**
23
+ * Hook to retrieve the BreadcrumbsController.
24
+ *
25
+ * Consider that in order to use this hook you need to have a parent
26
+ * `FireCMS`
27
+ *
28
+ * @see BreadcrumbsController
29
+ * @group Hooks and utilities
30
+ */
31
+ export const useBreadcrumbsController = (): BreadcrumbsController => useContext(BreadcrumbContext);
@@ -9,6 +9,7 @@ import {
9
9
  EntityCollection,
10
10
  EntityCollectionsBuilder,
11
11
  EntityReference,
12
+ NavigationBlocker,
12
13
  NavigationController,
13
14
  PermissionsBuilder,
14
15
  TopNavigationEntry,
@@ -26,6 +27,7 @@ import {
26
27
  resolvePermissions
27
28
  } from "../util";
28
29
  import { getParentReferencesFromPath } from "../util/parent_references_from_path";
30
+ import { useBlocker, useNavigate } from "react-router-dom";
29
31
 
30
32
  const DEFAULT_BASE_PATH = "/";
31
33
  const DEFAULT_COLLECTION_PATH = "/c";
@@ -66,6 +68,8 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
66
68
  injectCollections
67
69
  } = props;
68
70
 
71
+ const navigate = useNavigate();
72
+
69
73
  const collectionsRef = useRef<EntityCollection[] | undefined>();
70
74
  const viewsRef = useRef<CMSView[] | undefined>();
71
75
  const adminViewsRef = useRef<CMSView[] | undefined>();
@@ -332,16 +336,7 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
332
336
  throw Error("Expected path starting with " + fullCollectionPath);
333
337
  }, [fullCollectionPath]);
334
338
 
335
- const buildUrlEditCollectionPath = useCallback(({
336
- path
337
- }: {
338
- path: string
339
- }): string => {
340
- return `s/edit/${encodePath(path)}`;
341
- },
342
- []);
343
-
344
- const resolveAliasesFrom = useCallback((path: string): string => {
339
+ const resolveIdsFrom = useCallback((path: string): string => {
345
340
  const collections = collectionsRef.current ?? [];
346
341
  return resolveCollectionPathIds(path, collections);
347
342
  }, []);
@@ -401,24 +396,16 @@ export function useBuildNavigationController<EC extends EntityCollection, USER e
401
396
  isUrlCollectionPath,
402
397
  urlPathToDataPath,
403
398
  buildUrlCollectionPath,
404
- buildUrlEditCollectionPath,
405
- buildCMSUrlPath,
406
- resolveAliasesFrom,
399
+ resolveIdsFrom,
407
400
  topLevelNavigation,
408
401
  refreshNavigation,
409
402
  getParentReferencesFromPath: getAllParentReferencesForPath,
410
403
  getParentCollectionIds,
411
- convertIdsToPaths
404
+ convertIdsToPaths,
405
+ navigate,
412
406
  };
413
407
  }
414
408
 
415
- export function getSidePanelKey(path: string, entityId?: string) {
416
- if (entityId)
417
- return `${removeInitialAndTrailingSlashes(path)}/${removeInitialAndTrailingSlashes(entityId)}`;
418
- else
419
- return removeInitialAndTrailingSlashes(path);
420
- }
421
-
422
409
  function encodePath(input: string) {
423
410
  return encodeURIComponent(removeInitialAndTrailingSlashes(input))
424
411
  .replaceAll("%2F", "/")
@@ -513,3 +500,54 @@ function areCollectionsEqual(a: EntityCollection, b: EntityCollection) {
513
500
  }
514
501
  return equal(removeFunctions(restA), removeFunctions(restB));
515
502
  }
503
+
504
+ function useCustomBlocker(): NavigationBlocker {
505
+ const [blockListeners, setBlockListeners] = useState<Record<string, {
506
+ block: boolean,
507
+ basePath?: string
508
+ }>>({});
509
+
510
+ const shouldBlock = Object.values(blockListeners).some(b => b.block);
511
+
512
+ let blocker: any;
513
+ try {
514
+ blocker = useBlocker(({
515
+ nextLocation
516
+ }) => {
517
+ const allBasePaths = Object.values(blockListeners).map(b => b.basePath).filter(Boolean) as string[];
518
+ if (allBasePaths && allBasePaths.some(path => nextLocation.pathname.startsWith(path)))
519
+ return false;
520
+ return shouldBlock;
521
+ });
522
+ } catch (e) {
523
+ console.warn("Blocker not available, navigation will not be blocked");
524
+ }
525
+
526
+ const updateBlockListener = (path: string, block: boolean, basePath?: string) => {
527
+ setBlockListeners(prev => ({
528
+ ...prev,
529
+ [path]: {
530
+ block,
531
+ basePath
532
+ }
533
+ }));
534
+ return () => setBlockListeners(prev => {
535
+ const {
536
+ [path]: removed,
537
+ ...rest
538
+ } = prev;
539
+ return rest;
540
+ })
541
+ };
542
+
543
+ const isBlocked = (path: string) => {
544
+ return (blockListeners[path]?.block ?? false) && blocker?.state === "blocked";
545
+ }
546
+
547
+ return {
548
+ updateBlockListener,
549
+ isBlocked,
550
+ proceed: blocker?.proceed,
551
+ reset: blocker?.reset
552
+ }
553
+ }
@@ -63,38 +63,3 @@ function checkLargeLayout(breakpoint: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" |
63
63
  return false;
64
64
  return window.matchMedia(`(min-width: ${breakpoints[breakpoint] + 1}px)`).matches;
65
65
  }
66
-
67
- // import { useEffect, useState } from "react";
68
- //
69
- // const breakpoints = {
70
- // xs: 0,
71
- // sm: 640,
72
- // md: 768,
73
- // lg: 1024,
74
- // xl: 1280,
75
- // "2xl": 1536,
76
- // "3xl": 1920
77
- // }
78
- // export const useLargeLayout = (breakpoint: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" = "lg"): boolean => {
79
- // const [isLargeLayout, setIsLargeLayout] = useState<boolean>(false);
80
- //
81
- // useEffect(() => {
82
- // const handleResize = () => {
83
- // const matched = window.matchMedia(`(min-width: ${breakpoints[breakpoint] + 1}px)`).matches;
84
- // setIsLargeLayout(matched);
85
- // };
86
- //
87
- // // Set initial state
88
- // handleResize();
89
- //
90
- // // Set up event listener for resize events
91
- // window.addEventListener("resize", handleResize);
92
- //
93
- // // Clean up event listener when component unmounts
94
- // return () => {
95
- // window.removeEventListener("resize", handleResize);
96
- // };
97
- // }, []);
98
- //
99
- // return isLargeLayout;
100
- // };
@@ -1,6 +1,6 @@
1
1
  import { Entity, EntityCollection, EntityCustomView, FireCMSContext, User } from "../types";
2
2
  import { useEffect, useState } from "react";
3
- import { getNavigationEntriesFromPathInternal } from "../util/navigation_from_path";
3
+ import { getNavigationEntriesFromPath } from "../util/navigation_from_path";
4
4
  import { useFireCMSContext } from "./useFireCMSContext";
5
5
 
6
6
  /**
@@ -67,10 +67,10 @@ export function resolveNavigationFrom<M extends Record<string, any>, USER extend
67
67
  const navigation = context.navigation;
68
68
 
69
69
  if (!navigation) {
70
- throw Error("Calling getNavigationFrom, but main navigation has not yet been initialised");
70
+ throw Error("Calling resolveNavigationFrom, but main navigation has not yet been initialised");
71
71
  }
72
72
 
73
- const navigationEntries = getNavigationEntriesFromPathInternal({
73
+ const navigationEntries = getNavigationEntriesFromPath({
74
74
  path,
75
75
  collections: navigation.collections ?? []
76
76
  });
@@ -143,9 +143,7 @@ export function useResolvedNavigationFrom<M extends Record<string, any>, USER ex
143
143
  setDataLoading(true);
144
144
  setDataLoadingError(undefined);
145
145
  resolveNavigationFrom<M, USER>({ path, context })
146
- .then((res) => {
147
- setData(res);
148
- })
146
+ .then(setData)
149
147
  .catch((e) => setDataLoadingError(e))
150
148
  .finally(() => setDataLoading(false));
151
149
  }
@@ -25,8 +25,9 @@ export function useBuildSideDialogsController(): SideDialogsController {
25
25
  const newPanels = panelKeys
26
26
  .map(key => routesStore.current[key])
27
27
  .filter(p => Boolean(p)) as SideDialogPanelProps[];
28
- if (!equal(sidePanelsRef.current.map(p => p.key), newPanels.map(p => p.key)))
28
+ if (!equal(sidePanelsRef.current.map(p => p.key), newPanels.map(p => p.key))) {
29
29
  updateSidePanels(newPanels);
30
+ }
30
31
  }, [location]);
31
32
 
32
33
  const close = useCallback(() => {
@@ -39,7 +40,7 @@ export function useBuildSideDialogsController(): SideDialogsController {
39
40
  updateSidePanels(updatedPanels);
40
41
 
41
42
  if (routesCount.current > 0) {
42
- if (lastSidePanel.urlPath)
43
+ if (lastSidePanel.urlPath) // if it has a url path, we need to navigate back, don't remove this code
43
44
  navigate(-1);
44
45
  routesCount.current--;
45
46
  } else if (lastSidePanel.parentUrlPath) {
@@ -89,6 +90,7 @@ export function useBuildSideDialogsController(): SideDialogsController {
89
90
 
90
91
  const replace = useCallback((panelProps: SideDialogPanelProps | SideDialogPanelProps[]) => {
91
92
 
93
+ console.log("replace", panelProps);
92
94
  const newPanels: SideDialogPanelProps[] = Array.isArray(panelProps) ? panelProps : [panelProps];
93
95
  newPanels.forEach((panel) => {
94
96
  routesStore.current[panel.key] = panel;