@firecms/core 3.0.0-canary.4 → 3.0.0-canary.41
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.
- package/README.md +2 -2
- package/dist/components/ClearFilterSortButton.d.ts +5 -0
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
- package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +2 -2
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +12 -3
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
- package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
- package/dist/components/EntityPreview.d.ts +25 -7
- package/dist/components/EntityView.d.ts +11 -0
- package/dist/components/FieldCaption.d.ts +5 -0
- package/dist/components/HomePage/NavigationCard.d.ts +8 -0
- package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
- package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
- package/dist/components/HomePage/index.d.ts +3 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/components/index.d.ts +4 -3
- package/dist/contexts/AuthControllerContext.d.ts +1 -1
- package/dist/{internal/EntityView.d.ts → core/EntityEditView.d.ts} +2 -2
- package/dist/core/SideEntityView.d.ts +7 -0
- package/dist/core/index.d.ts +0 -2
- package/dist/form/EntityForm.d.ts +1 -1
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/form/components/StorageItemPreview.d.ts +3 -2
- package/dist/form/components/StorageUploadProgress.d.ts +1 -1
- package/dist/form/components/index.d.ts +1 -0
- package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -3
- package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
- package/dist/form/index.d.ts +1 -0
- package/dist/form/validation.d.ts +1 -1
- package/dist/hooks/data/delete.d.ts +2 -2
- package/dist/hooks/data/save.d.ts +1 -1
- package/dist/hooks/data/useDataSource.d.ts +2 -2
- package/dist/hooks/data/useEntityFetch.d.ts +3 -3
- package/dist/hooks/index.d.ts +3 -1
- package/dist/{core → hooks}/useBuildModeController.d.ts +1 -1
- package/dist/hooks/useBuildNavigationController.d.ts +6 -4
- package/dist/hooks/useProjectLog.d.ts +6 -2
- package/dist/hooks/useStorageSource.d.ts +2 -2
- package/dist/hooks/useValidateAuthenticator.d.ts +25 -0
- package/dist/index.es.js +8258 -7767
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +4 -0
- package/dist/preview/PropertyPreview.d.ts +1 -1
- package/dist/preview/PropertyPreviewProps.d.ts +1 -4
- package/dist/preview/components/BooleanPreview.d.ts +5 -1
- package/dist/preview/components/EnumValuesChip.d.ts +1 -1
- package/dist/preview/components/ReferencePreview.d.ts +1 -7
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/auth.d.ts +37 -1
- package/dist/types/collections.d.ts +22 -5
- package/dist/types/datasource.d.ts +1 -1
- package/dist/types/entities.d.ts +1 -1
- package/dist/types/entity_callbacks.d.ts +2 -2
- package/dist/types/entity_overrides.d.ts +6 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/navigation.d.ts +14 -13
- package/dist/types/permissions.d.ts +5 -1
- package/dist/types/plugins.d.ts +20 -20
- package/dist/types/properties.d.ts +2 -2
- package/dist/types/property_config.d.ts +2 -2
- package/dist/types/roles.d.ts +31 -0
- package/dist/types/storage.d.ts +11 -3
- package/dist/types/user.d.ts +5 -0
- package/dist/util/collections.d.ts +9 -1
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/icons.d.ts +8 -2
- package/dist/util/navigation_utils.d.ts +2 -2
- package/dist/util/permissions.d.ts +4 -4
- package/dist/util/references.d.ts +4 -2
- package/dist/util/resolutions.d.ts +6 -6
- package/dist/util/useTraceUpdate.d.ts +1 -0
- package/package.json +27 -24
- package/src/components/ClearFilterSortButton.tsx +41 -0
- package/src/components/DeleteEntityDialog.tsx +4 -4
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +268 -277
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +13 -13
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -16
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
- package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +9 -5
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +51 -52
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
- package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
- package/src/components/EntityPreview.tsx +207 -70
- package/src/components/EntityView.tsx +84 -0
- package/src/components/FieldCaption.tsx +14 -0
- package/src/components/FireCMSAppBar.tsx +8 -0
- package/src/components/HomePage/DefaultHomePage.tsx +14 -10
- package/src/components/HomePage/NavigationCard.tsx +69 -0
- package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
- package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
- package/src/components/HomePage/index.tsx +3 -1
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -4
- package/src/components/ReferenceWidget.tsx +4 -4
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +35 -24
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
- package/src/components/VirtualTable/VirtualTable.tsx +17 -7
- package/src/components/VirtualTable/VirtualTableProps.tsx +1 -1
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +1 -1
- package/src/components/index.tsx +4 -3
- package/src/contexts/AuthControllerContext.tsx +1 -1
- package/src/core/Drawer.tsx +66 -39
- package/src/{internal/EntityView.tsx → core/EntityEditView.tsx} +22 -39
- package/src/core/EntitySidePanel.tsx +2 -2
- package/src/core/FireCMS.tsx +18 -3
- package/src/core/NavigationRoutes.tsx +8 -0
- package/src/core/SideEntityView.tsx +38 -0
- package/src/core/field_configs.tsx +1 -2
- package/src/core/index.tsx +0 -2
- package/src/form/EntityForm.tsx +20 -12
- package/src/form/components/StorageItemPreview.tsx +5 -3
- package/src/form/components/StorageUploadProgress.tsx +6 -5
- package/src/form/components/index.tsx +1 -0
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +2 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +12 -15
- package/src/form/field_bindings/BlockFieldBinding.tsx +2 -3
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +4 -4
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +18 -18
- package/src/form/field_bindings/MapFieldBinding.tsx +17 -17
- package/src/form/field_bindings/MarkdownFieldBinding.tsx +1 -2
- package/src/form/field_bindings/MultiSelectBinding.tsx +2 -3
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +3 -3
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +5 -3
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -3
- package/src/form/field_bindings/SelectFieldBinding.tsx +2 -3
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +15 -6
- package/src/form/field_bindings/SwitchFieldBinding.tsx +2 -3
- package/src/form/field_bindings/TextFieldBinding.tsx +10 -9
- package/src/form/index.tsx +1 -0
- package/src/form/validation.ts +3 -4
- package/src/hooks/data/delete.ts +3 -3
- package/src/hooks/data/save.ts +1 -1
- package/src/hooks/data/useCollectionFetch.tsx +1 -1
- package/src/hooks/data/useDataSource.tsx +8 -3
- package/src/hooks/data/useEntityFetch.tsx +4 -4
- package/src/hooks/index.tsx +5 -1
- package/src/{core → hooks}/useBuildLocalConfigurationPersistence.tsx +9 -10
- package/src/{core → hooks}/useBuildModeController.tsx +12 -6
- package/src/hooks/useBuildNavigationController.tsx +197 -79
- package/src/hooks/useProjectLog.tsx +17 -7
- package/src/hooks/useReferenceDialog.tsx +2 -2
- package/src/hooks/useStorageSource.tsx +7 -2
- package/src/hooks/useValidateAuthenticator.tsx +135 -0
- package/src/internal/useBuildDataSource.ts +6 -1
- package/src/internal/useBuildSideEntityController.tsx +18 -12
- package/src/preview/PropertyPreview.tsx +1 -1
- package/src/preview/PropertyPreviewProps.tsx +1 -11
- package/src/preview/components/BooleanPreview.tsx +19 -4
- package/src/preview/components/EnumValuesChip.tsx +1 -1
- package/src/preview/components/ReferencePreview.tsx +55 -147
- package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
- package/src/types/analytics.ts +1 -0
- package/src/types/auth.tsx +50 -1
- package/src/types/collections.ts +24 -5
- package/src/types/datasource.ts +1 -1
- package/src/types/entities.ts +1 -1
- package/src/types/entity_actions.tsx +4 -0
- package/src/types/entity_callbacks.ts +2 -2
- package/src/types/entity_overrides.tsx +7 -0
- package/src/types/firecms.tsx +0 -1
- package/src/types/index.ts +2 -0
- package/src/types/navigation.ts +17 -16
- package/src/types/permissions.ts +6 -1
- package/src/types/plugins.tsx +26 -28
- package/src/types/properties.ts +3 -2
- package/src/types/property_config.tsx +2 -2
- package/src/types/roles.ts +41 -0
- package/src/types/side_entity_controller.tsx +1 -0
- package/src/types/storage.ts +12 -3
- package/src/types/user.ts +7 -0
- package/src/util/collections.ts +22 -0
- package/src/util/entities.ts +1 -1
- package/src/util/icons.tsx +11 -3
- package/src/util/navigation_utils.ts +6 -6
- package/src/util/permissions.ts +11 -8
- package/src/util/references.ts +36 -5
- package/src/util/strings.ts +2 -2
- package/src/util/useTraceUpdate.tsx +2 -1
- package/dist/internal/useLocaleConfig.d.ts +0 -1
- package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
- package/src/internal/useLocaleConfig.tsx +0 -18
- /package/dist/{components → form/components}/LabelWithIcon.d.ts +0 -0
- /package/dist/{core → hooks}/useBuildLocalConfigurationPersistence.d.ts +0 -0
- /package/src/{components → form/components}/LabelWithIcon.tsx +0 -0
package/src/hooks/index.tsx
CHANGED
|
@@ -4,7 +4,6 @@ export * from "./data/useEntityFetch";
|
|
|
4
4
|
export * from "./data/save";
|
|
5
5
|
export * from "./data/delete";
|
|
6
6
|
|
|
7
|
-
export * from "../form/useClearRestoreValue";
|
|
8
7
|
export * from "./useNavigationController";
|
|
9
8
|
|
|
10
9
|
export * from "./useResolvedNavigationFrom";
|
|
@@ -23,3 +22,8 @@ export * from "./useReferenceDialog";
|
|
|
23
22
|
export * from "./useBrowserTitleAndIcon";
|
|
24
23
|
export * from "./useCustomizationController";
|
|
25
24
|
export * from "./useBuildNavigationController";
|
|
25
|
+
|
|
26
|
+
export * from "./useBuildLocalConfigurationPersistence";
|
|
27
|
+
export * from "./useBuildModeController";
|
|
28
|
+
|
|
29
|
+
export * from "./useValidateAuthenticator";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from "react";
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { PartialEntityCollection, UserConfigurationPersistence } from "../types";
|
|
3
3
|
import { mergeDeep, stripCollectionPath } from "../util";
|
|
4
4
|
|
|
5
5
|
export function useBuildLocalConfigurationPersistence(): UserConfigurationPersistence {
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const configCache = useRef<Record<string, PartialEntityCollection>>({});
|
|
8
8
|
|
|
9
9
|
const getCollectionFromStorage = useCallback((storageKey: string) => {
|
|
10
10
|
const item = localStorage.getItem(storageKey);
|
|
@@ -13,20 +13,19 @@ export function useBuildLocalConfigurationPersistence(): UserConfigurationPersis
|
|
|
13
13
|
|
|
14
14
|
const getCollectionConfig = useCallback(<M extends Record<string, any>>(path: string): PartialEntityCollection<M> => {
|
|
15
15
|
const storageKey = `collection_config::${stripCollectionPath(path)}`;
|
|
16
|
-
if (configCache[storageKey]) {
|
|
17
|
-
return configCache[storageKey] as PartialEntityCollection<M>;
|
|
16
|
+
if (configCache.current[storageKey]) {
|
|
17
|
+
return configCache.current[storageKey] as PartialEntityCollection<M>;
|
|
18
18
|
}
|
|
19
19
|
return getCollectionFromStorage(storageKey);
|
|
20
|
-
}, [
|
|
20
|
+
}, [getCollectionFromStorage]);
|
|
21
21
|
|
|
22
22
|
const onCollectionModified = useCallback(<M extends Record<string, any>>(path: string, data: PartialEntityCollection<M>) => {
|
|
23
23
|
const storageKey = `collection_config::${stripCollectionPath(path)}`;
|
|
24
24
|
localStorage.setItem(storageKey, JSON.stringify(data));
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
25
|
+
const currentCache = configCache.current;
|
|
26
|
+
const cachedConfig = currentCache[storageKey];
|
|
27
|
+
const newConfig = mergeDeep(cachedConfig ?? getCollectionFromStorage(path), data);
|
|
28
|
+
configCache.current = mergeDeep(currentCache, newConfig);
|
|
30
29
|
}, [getCollectionFromStorage]);
|
|
31
30
|
|
|
32
31
|
const [recentlyVisitedPaths, _setRecentlyVisitedPaths] = useState<string[]>([]);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import { ModeController } from "
|
|
3
|
+
import { ModeController } from "./index";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Use this hook to build a color mode controller that determines
|
|
@@ -8,8 +8,13 @@ import { ModeController } from "../hooks";
|
|
|
8
8
|
*/
|
|
9
9
|
export function useBuildModeController(): ModeController {
|
|
10
10
|
|
|
11
|
-
const prefersDarkModeQuery =
|
|
12
|
-
|
|
11
|
+
const prefersDarkModeQuery = useCallback((): boolean => {
|
|
12
|
+
if (typeof window === "undefined")
|
|
13
|
+
return false;
|
|
14
|
+
const mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
|
|
15
|
+
return mediaQueryList.matches;
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
13
18
|
const prefersDarkModeStorage: boolean | null = localStorage.getItem("prefers-dark-mode") != null ? localStorage.getItem("prefers-dark-mode") === "true" : null;
|
|
14
19
|
const prefersDarkMode = prefersDarkModeStorage ?? prefersDarkModeQuery;
|
|
15
20
|
const [mode, setMode] = useState<"light" | "dark">(prefersDarkMode ? "dark" : "light");
|
|
@@ -23,7 +28,7 @@ export function useBuildModeController(): ModeController {
|
|
|
23
28
|
const setDarkMode = useCallback(() => {
|
|
24
29
|
setMode("dark");
|
|
25
30
|
setDocumentMode("dark");
|
|
26
|
-
}, [
|
|
31
|
+
}, []);
|
|
27
32
|
|
|
28
33
|
const setLightMode = useCallback(() => {
|
|
29
34
|
setMode("light");
|
|
@@ -37,14 +42,15 @@ export function useBuildModeController(): ModeController {
|
|
|
37
42
|
|
|
38
43
|
const toggleMode = useCallback(() => {
|
|
39
44
|
|
|
45
|
+
const prefersDarkModeQueryResult = prefersDarkModeQuery();
|
|
40
46
|
if (mode === "light") {
|
|
41
|
-
if (!
|
|
47
|
+
if (!prefersDarkModeQueryResult)
|
|
42
48
|
localStorage.setItem("prefers-dark-mode", "true");
|
|
43
49
|
else
|
|
44
50
|
localStorage.removeItem("prefers-dark-mode");
|
|
45
51
|
setDarkMode();
|
|
46
52
|
} else {
|
|
47
|
-
if (
|
|
53
|
+
if (prefersDarkModeQueryResult)
|
|
48
54
|
localStorage.setItem("prefers-dark-mode", "false");
|
|
49
55
|
else
|
|
50
56
|
localStorage.removeItem("prefers-dark-mode");
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
import { useLocation } from "react-router-dom";
|
|
3
2
|
import equal from "react-fast-compare"
|
|
4
3
|
|
|
5
4
|
import {
|
|
@@ -11,12 +10,14 @@ import {
|
|
|
11
10
|
EntityCollectionsBuilder,
|
|
12
11
|
EntityReference,
|
|
13
12
|
NavigationController,
|
|
13
|
+
PermissionsBuilder,
|
|
14
14
|
TopNavigationEntry,
|
|
15
15
|
TopNavigationResult,
|
|
16
16
|
User,
|
|
17
17
|
UserConfigurationPersistence
|
|
18
18
|
} from "../types";
|
|
19
19
|
import {
|
|
20
|
+
applyPermissionsFunctionIfEmpty,
|
|
20
21
|
getCollectionByPathOrId,
|
|
21
22
|
mergeDeep,
|
|
22
23
|
removeInitialAndTrailingSlashes,
|
|
@@ -28,12 +29,15 @@ import { getParentReferencesFromPath } from "../util/parent_references_from_path
|
|
|
28
29
|
const DEFAULT_BASE_PATH = "/";
|
|
29
30
|
const DEFAULT_COLLECTION_PATH = "/c";
|
|
30
31
|
|
|
31
|
-
type BuildNavigationContextProps<EC extends EntityCollection, UserType extends User> = {
|
|
32
|
+
export type BuildNavigationContextProps<EC extends EntityCollection, UserType extends User> = {
|
|
32
33
|
basePath?: string,
|
|
33
34
|
baseCollectionPath?: string,
|
|
34
35
|
authController: AuthController<UserType>;
|
|
35
36
|
collections?: EC[] | EntityCollectionsBuilder<EC>;
|
|
37
|
+
collectionPermissions?: PermissionsBuilder;
|
|
36
38
|
views?: CMSView[] | CMSViewsBuilder;
|
|
39
|
+
adminViews?: CMSView[] | CMSViewsBuilder;
|
|
40
|
+
viewsOrder?: string[];
|
|
37
41
|
userConfigPersistence?: UserConfigurationPersistence;
|
|
38
42
|
dataSourceDelegate: DataSourceDelegate;
|
|
39
43
|
/**
|
|
@@ -46,22 +50,25 @@ type BuildNavigationContextProps<EC extends EntityCollection, UserType extends U
|
|
|
46
50
|
injectCollections?: (collections: EntityCollection[]) => EntityCollection[];
|
|
47
51
|
};
|
|
48
52
|
|
|
49
|
-
export function useBuildNavigationController<EC extends EntityCollection, UserType extends User>({
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
53
|
+
export function useBuildNavigationController<EC extends EntityCollection, UserType extends User>(props: BuildNavigationContextProps<EC, UserType>): NavigationController {
|
|
54
|
+
const {
|
|
55
|
+
basePath = DEFAULT_BASE_PATH,
|
|
56
|
+
baseCollectionPath = DEFAULT_COLLECTION_PATH,
|
|
57
|
+
authController,
|
|
58
|
+
collections: collectionsProp,
|
|
59
|
+
collectionPermissions,
|
|
60
|
+
views: viewsProp,
|
|
61
|
+
adminViews: adminViewsProp,
|
|
62
|
+
viewsOrder,
|
|
63
|
+
userConfigPersistence,
|
|
64
|
+
dataSourceDelegate,
|
|
65
|
+
injectCollections
|
|
66
|
+
} = props;
|
|
67
|
+
|
|
68
|
+
const collectionsRef = useRef<EntityCollection[] | undefined>();
|
|
69
|
+
const viewsRef = useRef<CMSView[] | undefined>();
|
|
70
|
+
const adminViewsRef = useRef<CMSView[] | undefined>();
|
|
71
|
+
|
|
65
72
|
const [initialised, setInitialised] = useState<boolean>(false);
|
|
66
73
|
|
|
67
74
|
const [topLevelNavigation, setTopLevelNavigation] = useState<TopNavigationResult | undefined>(undefined);
|
|
@@ -81,18 +88,18 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
81
88
|
const buildUrlCollectionPath = useCallback((path: string): string => `${removeInitialAndTrailingSlashes(baseCollectionPath)}/${encodePath(path)}`,
|
|
82
89
|
[baseCollectionPath]);
|
|
83
90
|
|
|
84
|
-
const computeTopNavigation = useCallback((collections: EntityCollection[], views: CMSView[]): TopNavigationResult => {
|
|
85
|
-
|
|
91
|
+
const computeTopNavigation = useCallback((collections: EntityCollection[], views: CMSView[], adminViews: CMSView[], viewsOrder?: string[]): TopNavigationResult => {
|
|
92
|
+
let navigationEntries: TopNavigationEntry[] = [
|
|
86
93
|
...(collections ?? []).map(collection => (!collection.hideFromNavigation
|
|
87
|
-
? {
|
|
94
|
+
? ({
|
|
88
95
|
url: buildUrlCollectionPath(collection.id ?? collection.path),
|
|
89
96
|
type: "collection",
|
|
90
97
|
name: collection.name.trim(),
|
|
91
98
|
path: collection.id ?? collection.path,
|
|
92
99
|
collection,
|
|
93
100
|
description: collection.description?.trim(),
|
|
94
|
-
group: collection
|
|
95
|
-
}
|
|
101
|
+
group: getGroup(collection)
|
|
102
|
+
} satisfies TopNavigationEntry)
|
|
96
103
|
: undefined))
|
|
97
104
|
.filter(Boolean) as TopNavigationEntry[],
|
|
98
105
|
...(views ?? []).map(view =>
|
|
@@ -101,18 +108,74 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
101
108
|
url: buildCMSUrlPath(Array.isArray(view.path) ? view.path[0] : view.path),
|
|
102
109
|
name: view.name.trim(),
|
|
103
110
|
type: "view",
|
|
111
|
+
path: view.path,
|
|
104
112
|
view,
|
|
105
113
|
description: view.description?.trim(),
|
|
106
|
-
group: view
|
|
107
|
-
})
|
|
114
|
+
group: getGroup(view)
|
|
115
|
+
} satisfies TopNavigationEntry)
|
|
116
|
+
: undefined)
|
|
117
|
+
.filter(Boolean) as TopNavigationEntry[],
|
|
118
|
+
...(adminViews ?? []).map(view =>
|
|
119
|
+
!view.hideFromNavigation
|
|
120
|
+
? ({
|
|
121
|
+
url: buildCMSUrlPath(Array.isArray(view.path) ? view.path[0] : view.path),
|
|
122
|
+
name: view.name.trim(),
|
|
123
|
+
type: "admin",
|
|
124
|
+
path: view.path,
|
|
125
|
+
view,
|
|
126
|
+
description: view.description?.trim(),
|
|
127
|
+
group: "Admin"
|
|
128
|
+
} satisfies TopNavigationEntry)
|
|
108
129
|
: undefined)
|
|
109
130
|
.filter(Boolean) as TopNavigationEntry[]
|
|
110
131
|
];
|
|
111
132
|
|
|
133
|
+
// Sort by group, entries with group "Admin" will go last, and second to last will be the group "Views"
|
|
134
|
+
navigationEntries = navigationEntries.sort((a, b) => {
|
|
135
|
+
if (a.group !== "Views" && a.group !== "Admin" && (b.group === "Views" || b.group === "Admin")) {
|
|
136
|
+
return -1;
|
|
137
|
+
}
|
|
138
|
+
if (b.group !== "Views" && b.group !== "Admin" && (a.group === "Views" || a.group === "Admin")) {
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
if (a.group === "Admin" && b.group !== "Admin") {
|
|
142
|
+
return 1;
|
|
143
|
+
}
|
|
144
|
+
if (a.group !== "Admin" && b.group === "Admin") {
|
|
145
|
+
return -1;
|
|
146
|
+
}
|
|
147
|
+
if (a.group === "Views" && b.group !== "Views") {
|
|
148
|
+
return -1;
|
|
149
|
+
}
|
|
150
|
+
if (a.group !== "Views" && b.group === "Views") {
|
|
151
|
+
return 1;
|
|
152
|
+
}
|
|
153
|
+
return 0;
|
|
154
|
+
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
if (viewsOrder) {
|
|
158
|
+
navigationEntries = navigationEntries.sort((a, b) => {
|
|
159
|
+
const aIndex = viewsOrder.indexOf(a.path);
|
|
160
|
+
const bIndex = viewsOrder.indexOf(b.path);
|
|
161
|
+
if (aIndex === -1 && bIndex === -1) {
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
if (aIndex === -1) {
|
|
165
|
+
return 1;
|
|
166
|
+
}
|
|
167
|
+
if (bIndex === -1) {
|
|
168
|
+
return -1;
|
|
169
|
+
}
|
|
170
|
+
return aIndex - bIndex;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
112
174
|
const groups: string[] = Object.values(navigationEntries)
|
|
113
175
|
.map(e => e.group)
|
|
114
176
|
.filter(Boolean)
|
|
115
177
|
.filter((value, index, array) => array.indexOf(value) === index) as string[];
|
|
178
|
+
|
|
116
179
|
return {
|
|
117
180
|
navigationEntries,
|
|
118
181
|
groups
|
|
@@ -124,17 +187,34 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
124
187
|
if (authController.initialLoading)
|
|
125
188
|
return;
|
|
126
189
|
|
|
190
|
+
console.debug("Refreshing navigation");
|
|
191
|
+
|
|
127
192
|
try {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
193
|
+
|
|
194
|
+
const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([
|
|
195
|
+
resolveCollections(collectionsProp, collectionPermissions, authController, dataSourceDelegate, injectCollections),
|
|
196
|
+
resolveCMSViews(viewsProp, authController, dataSourceDelegate),
|
|
197
|
+
resolveCMSViews(adminViewsProp, authController, dataSourceDelegate)
|
|
131
198
|
]
|
|
132
199
|
);
|
|
133
|
-
|
|
200
|
+
|
|
201
|
+
let shouldUpdateTopLevelNav = false;
|
|
202
|
+
if (!areCollectionListsEqual(collectionsRef.current ?? [], resolvedCollections)) {
|
|
134
203
|
collectionsRef.current = resolvedCollections;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
204
|
+
shouldUpdateTopLevelNav = true;
|
|
205
|
+
}
|
|
206
|
+
if (!equal(viewsRef.current, resolvedViews)) {
|
|
207
|
+
viewsRef.current = resolvedViews;
|
|
208
|
+
shouldUpdateTopLevelNav = true;
|
|
209
|
+
}
|
|
210
|
+
if (!equal(adminViewsRef.current, resolvedAdminViews)) {
|
|
211
|
+
adminViewsRef.current = resolvedAdminViews;
|
|
212
|
+
shouldUpdateTopLevelNav = true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const computedTopLevelNav = computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder);
|
|
216
|
+
if (shouldUpdateTopLevelNav && !equal(topLevelNavigation, computedTopLevelNav)) {
|
|
217
|
+
setTopLevelNavigation(computedTopLevelNav);
|
|
138
218
|
}
|
|
139
219
|
} catch (e) {
|
|
140
220
|
console.error(e);
|
|
@@ -143,25 +223,34 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
143
223
|
|
|
144
224
|
setNavigationLoading(false);
|
|
145
225
|
setInitialised(true);
|
|
146
|
-
|
|
226
|
+
|
|
227
|
+
}, [
|
|
228
|
+
collectionsProp,
|
|
229
|
+
collectionPermissions,
|
|
230
|
+
authController.user,
|
|
231
|
+
authController.initialLoading,
|
|
232
|
+
viewsProp,
|
|
233
|
+
adminViewsProp,
|
|
234
|
+
computeTopNavigation,
|
|
235
|
+
injectCollections
|
|
236
|
+
]);
|
|
147
237
|
|
|
148
238
|
useEffect(() => {
|
|
149
239
|
refreshNavigation();
|
|
150
240
|
}, [refreshNavigation]);
|
|
151
241
|
|
|
152
|
-
const getCollection = useCallback(
|
|
242
|
+
const getCollection = useCallback((
|
|
153
243
|
idOrPath: string,
|
|
154
244
|
entityId?: string,
|
|
155
245
|
includeUserOverride = false
|
|
156
246
|
): EC | undefined => {
|
|
157
|
-
|
|
247
|
+
const collections = collectionsRef.current;
|
|
158
248
|
if (!collections)
|
|
159
249
|
return undefined;
|
|
160
250
|
|
|
161
251
|
const baseCollection = getCollectionByPathOrId(removeInitialAndTrailingSlashes(idOrPath), collections);
|
|
162
252
|
|
|
163
253
|
const userOverride = includeUserOverride ? userConfigPersistence?.getCollectionConfig(idOrPath) : undefined;
|
|
164
|
-
|
|
165
254
|
const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride) : undefined;
|
|
166
255
|
|
|
167
256
|
let result: Partial<EntityCollection> | undefined = overriddenCollection;
|
|
@@ -182,16 +271,14 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
182
271
|
|
|
183
272
|
return { ...overriddenCollection, ...result } as EC;
|
|
184
273
|
|
|
185
|
-
}, [
|
|
186
|
-
basePath,
|
|
187
|
-
baseCollectionPath,
|
|
188
|
-
collections,
|
|
189
|
-
]);
|
|
274
|
+
}, [userConfigPersistence]);
|
|
190
275
|
|
|
191
276
|
const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
277
|
+
|
|
278
|
+
const collections = collectionsRef.current;
|
|
279
|
+
if (collections === undefined)
|
|
280
|
+
throw Error("getCollectionFromPaths: Collections have not been initialised yet");
|
|
281
|
+
let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
|
|
195
282
|
|
|
196
283
|
for (let i = 0; i < pathSegments.length; i++) {
|
|
197
284
|
const pathSegment = pathSegments[i];
|
|
@@ -205,12 +292,14 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
205
292
|
|
|
206
293
|
return undefined;
|
|
207
294
|
|
|
208
|
-
}, [
|
|
295
|
+
}, []);
|
|
209
296
|
|
|
210
297
|
const getCollectionFromIds = useCallback(<EC extends EntityCollection>(ids: string[]): EC | undefined => {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
298
|
+
|
|
299
|
+
const collections = collectionsRef.current;
|
|
300
|
+
if (collections === undefined)
|
|
301
|
+
throw Error("getCollectionFromIds: Collections have not been initialised yet");
|
|
302
|
+
let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
|
|
214
303
|
|
|
215
304
|
for (let i = 0; i < ids.length; i++) {
|
|
216
305
|
const id = ids[i];
|
|
@@ -224,7 +313,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
224
313
|
|
|
225
314
|
return undefined;
|
|
226
315
|
|
|
227
|
-
}, [
|
|
316
|
+
}, []);
|
|
228
317
|
|
|
229
318
|
const isUrlCollectionPath = useCallback(
|
|
230
319
|
(path: string): boolean => removeInitialAndTrailingSlashes(path + "/").startsWith(removeInitialAndTrailingSlashes(fullCollectionPath) + "/"),
|
|
@@ -246,25 +335,17 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
246
335
|
[]);
|
|
247
336
|
|
|
248
337
|
const resolveAliasesFrom = useCallback((path: string): string => {
|
|
249
|
-
|
|
250
|
-
throw Error("Collections have not been initialised yet");
|
|
338
|
+
const collections = collectionsRef.current ?? [];
|
|
251
339
|
return resolveCollectionPathIds(path, collections);
|
|
252
|
-
}, [
|
|
253
|
-
|
|
254
|
-
const state = location.state as any;
|
|
255
|
-
/**
|
|
256
|
-
* The location can be overridden if `base_location` is set in the
|
|
257
|
-
* state field of the current location. This can happen if you open
|
|
258
|
-
* a side entity, like `products`, from a different one, like `users`
|
|
259
|
-
*/
|
|
260
|
-
const baseLocation = state && state.base_location ? state.base_location : location;
|
|
340
|
+
}, []);
|
|
261
341
|
|
|
262
342
|
const getAllParentReferencesForPath = useCallback((path: string): EntityReference[] => {
|
|
343
|
+
const collections = collectionsRef.current ?? [];
|
|
263
344
|
return getParentReferencesFromPath({
|
|
264
345
|
path,
|
|
265
346
|
collections
|
|
266
347
|
});
|
|
267
|
-
}, [
|
|
348
|
+
}, []);
|
|
268
349
|
|
|
269
350
|
const getParentCollectionIds = useCallback((path: string): string[] => {
|
|
270
351
|
|
|
@@ -283,23 +364,24 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
283
364
|
}, [getAllParentReferencesForPath])
|
|
284
365
|
|
|
285
366
|
const convertIdsToPaths = useCallback((ids: string[]): string[] => {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
return paths;
|
|
367
|
+
const collections = collectionsRef.current;
|
|
368
|
+
let currentCollections = collections;
|
|
369
|
+
const paths: string[] = [];
|
|
370
|
+
for (let i = 0; i < ids.length; i++) {
|
|
371
|
+
const id = ids[i];
|
|
372
|
+
const collection: EntityCollection | undefined = currentCollections!.find(c => c.id === id);
|
|
373
|
+
if (!collection)
|
|
374
|
+
throw Error(`Collection with id ${id} not found`);
|
|
375
|
+
paths.push(collection.path);
|
|
376
|
+
currentCollections = collection.subcollections;
|
|
297
377
|
}
|
|
298
|
-
|
|
378
|
+
return paths;
|
|
379
|
+
}, [getCollectionFromIds]);
|
|
299
380
|
|
|
300
381
|
return {
|
|
301
|
-
collections,
|
|
302
|
-
views,
|
|
382
|
+
collections: collectionsRef.current,
|
|
383
|
+
views: viewsRef.current,
|
|
384
|
+
adminViews: adminViewsRef.current,
|
|
303
385
|
loading: !initialised || navigationLoading,
|
|
304
386
|
navigationLoadingError,
|
|
305
387
|
homeUrl,
|
|
@@ -316,7 +398,6 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
316
398
|
buildCMSUrlPath,
|
|
317
399
|
resolveAliasesFrom,
|
|
318
400
|
topLevelNavigation,
|
|
319
|
-
baseLocation,
|
|
320
401
|
refreshNavigation,
|
|
321
402
|
getParentReferencesFromPath: getAllParentReferencesForPath,
|
|
322
403
|
getParentCollectionIds,
|
|
@@ -341,8 +422,8 @@ function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[],
|
|
|
341
422
|
return resolvedCollections
|
|
342
423
|
.filter((c) => {
|
|
343
424
|
if (!c.permissions) return true;
|
|
344
|
-
const resolvedPermissions = resolvePermissions(c, authController,
|
|
345
|
-
return resolvedPermissions
|
|
425
|
+
const resolvedPermissions = resolvePermissions(c, authController, c.path, null)
|
|
426
|
+
return resolvedPermissions?.read !== false;
|
|
346
427
|
})
|
|
347
428
|
.map((c) => {
|
|
348
429
|
if (!c.subcollections) return c;
|
|
@@ -354,9 +435,10 @@ function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[],
|
|
|
354
435
|
}
|
|
355
436
|
|
|
356
437
|
async function resolveCollections(collections: undefined | EntityCollection[] | EntityCollectionsBuilder<any>,
|
|
438
|
+
collectionPermissions: PermissionsBuilder | undefined,
|
|
357
439
|
authController: AuthController,
|
|
358
440
|
dataSource: DataSourceDelegate,
|
|
359
|
-
injectCollections?: (collections: EntityCollection[]) => EntityCollection[]) {
|
|
441
|
+
injectCollections?: (collections: EntityCollection[]) => EntityCollection[]): Promise<EntityCollection[]> {
|
|
360
442
|
let resolvedCollections: EntityCollection[] = [];
|
|
361
443
|
if (typeof collections === "function") {
|
|
362
444
|
resolvedCollections = await collections({
|
|
@@ -368,6 +450,8 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
|
|
|
368
450
|
resolvedCollections = collections;
|
|
369
451
|
}
|
|
370
452
|
|
|
453
|
+
resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
|
|
454
|
+
|
|
371
455
|
resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
|
|
372
456
|
|
|
373
457
|
if (injectCollections) {
|
|
@@ -390,3 +474,37 @@ async function resolveCMSViews(baseViews: CMSView[] | CMSViewsBuilder | undefine
|
|
|
390
474
|
}
|
|
391
475
|
return resolvedViews;
|
|
392
476
|
}
|
|
477
|
+
|
|
478
|
+
function getGroup(collectionOrView: EntityCollection<any, any> | CMSView) {
|
|
479
|
+
const trimmed = collectionOrView.group?.trim();
|
|
480
|
+
if (!trimmed || trimmed === "") {
|
|
481
|
+
return "Views";
|
|
482
|
+
}
|
|
483
|
+
return trimmed ?? "Views";
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function areCollectionListsEqual(a: EntityCollection[], b: EntityCollection[]) {
|
|
487
|
+
if (a.length !== b.length) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
const aCopy = [...a];
|
|
491
|
+
const bCopy = [...b];
|
|
492
|
+
const aSorted = aCopy.sort((x, y) => x.id.localeCompare(y.id));
|
|
493
|
+
const bSorted = bCopy.sort((x, y) => x.id.localeCompare(y.id));
|
|
494
|
+
return aSorted.every((value, index) => areCollectionsEqual(value, bSorted[index]));
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function areCollectionsEqual(a: EntityCollection, b: EntityCollection) {
|
|
498
|
+
const {
|
|
499
|
+
subcollections: subcollectionsA,
|
|
500
|
+
...restA
|
|
501
|
+
} = a;
|
|
502
|
+
const {
|
|
503
|
+
subcollections: subcollectionsB,
|
|
504
|
+
...restB
|
|
505
|
+
} = b;
|
|
506
|
+
if (!areCollectionListsEqual(subcollectionsA ?? [], subcollectionsB ?? [])) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
return equal(restA, restB);
|
|
510
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
2
|
-
import { AuthController } from "../types";
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { AuthController, FireCMSPlugin } from "../types";
|
|
3
3
|
|
|
4
4
|
export const DEFAULT_SERVER_DEV = "https://api-kdoe6pj3qq-ey.a.run.app";
|
|
5
5
|
export const DEFAULT_SERVER = "https://api-drplyi3b6q-ey.a.run.app";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
export type AccessResponse = {
|
|
8
|
+
blocked?: boolean;
|
|
9
|
+
message?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function makeRequest(authController: AuthController, pluginKeys: string[] | undefined) {
|
|
8
13
|
const firebaseToken = await authController.getAuthToken();
|
|
9
14
|
return fetch(DEFAULT_SERVER + "/access_log",
|
|
10
15
|
{
|
|
@@ -14,18 +19,23 @@ async function makeRequest(authController: AuthController) {
|
|
|
14
19
|
"Content-Type": "application/json",
|
|
15
20
|
Authorization: `Basic ${firebaseToken}`,
|
|
16
21
|
},
|
|
17
|
-
body: JSON.stringify({})
|
|
22
|
+
body: JSON.stringify({ plugins: pluginKeys })
|
|
18
23
|
})
|
|
19
24
|
.then(async (res) => {
|
|
25
|
+
return res.json();
|
|
20
26
|
});
|
|
21
27
|
}
|
|
22
28
|
|
|
23
|
-
export function useProjectLog(authController: AuthController
|
|
29
|
+
export function useProjectLog(authController: AuthController,
|
|
30
|
+
plugins?: FireCMSPlugin<any, any, any>[]): AccessResponse | null {
|
|
31
|
+
const [accessResponse, setAccessResponse] = useState<AccessResponse | null>(null);
|
|
24
32
|
const accessedUserRef = useRef<string | null>(null);
|
|
33
|
+
const pluginKeys = plugins?.map(plugin => plugin.key);
|
|
25
34
|
useEffect(() => {
|
|
26
35
|
if (authController.user && authController.user.uid !== accessedUserRef.current && !authController.initialLoading) {
|
|
27
|
-
makeRequest(authController);
|
|
36
|
+
makeRequest(authController, pluginKeys).then(setAccessResponse);
|
|
28
37
|
accessedUserRef.current = authController.user.uid;
|
|
29
38
|
}
|
|
30
|
-
}, [authController]);
|
|
39
|
+
}, [authController, pluginKeys]);
|
|
40
|
+
return accessResponse;
|
|
31
41
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useSideDialogsController } from "./useSideDialogsController";
|
|
2
|
-
import {
|
|
2
|
+
import { ReferenceSelectionInnerProps, ReferenceSelectionTable } from "../components";
|
|
3
3
|
import { useCallback } from "react";
|
|
4
4
|
import { useNavigationController } from "./useNavigationController";
|
|
5
5
|
|
|
@@ -27,7 +27,7 @@ export function useReferenceDialog<M extends Record<string, any>>(referenceDialo
|
|
|
27
27
|
if (!usedCollection)
|
|
28
28
|
usedCollection = navigation.getCollection(referenceDialogProps.path);
|
|
29
29
|
if (!usedCollection)
|
|
30
|
-
throw Error("Not able to resolve the collection in useReferenceDialog");
|
|
30
|
+
throw Error("Not able to resolve the collection in useReferenceDialog. Make sure a collection is registered in path " + referenceDialogProps.path);
|
|
31
31
|
sideDialogsController.open({
|
|
32
32
|
key: `reference_${referenceDialogProps.path}`,
|
|
33
33
|
component:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StorageSource } from "../types";
|
|
1
|
+
import { EntityCollection, StorageSource } from "../types";
|
|
2
2
|
import { StorageSourceContext } from "../contexts/StorageSourceContext";
|
|
3
3
|
import { useContext } from "react";
|
|
4
4
|
|
|
@@ -6,4 +6,9 @@ import { useContext } from "react";
|
|
|
6
6
|
* Use this hook to get the storage source being used
|
|
7
7
|
* @group Hooks and utilities
|
|
8
8
|
*/
|
|
9
|
-
export const useStorageSource = (): StorageSource =>
|
|
9
|
+
export const useStorageSource = (collection?: EntityCollection): StorageSource => {
|
|
10
|
+
const defaultStorageSource = useContext(StorageSourceContext);
|
|
11
|
+
if (collection?.overrides?.storageSource)
|
|
12
|
+
return collection.overrides.storageSource;
|
|
13
|
+
return defaultStorageSource;
|
|
14
|
+
};
|