@firecms/core 3.0.0-canary.40 → 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/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +11 -11
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +11 -1
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/index.es.js +2356 -2362
- 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/types/navigation.d.ts +1 -1
- package/dist/util/navigation_utils.d.ts +2 -2
- package/dist/util/resolutions.d.ts +5 -5
- package/package.json +8 -5
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +13 -4
- package/src/components/EntityView.tsx +1 -1
- package/src/components/VirtualTable/VirtualTable.tsx +17 -7
- package/src/core/FireCMS.tsx +1 -2
- package/src/hooks/useBuildNavigationController.tsx +10 -10
- package/src/hooks/useProjectLog.tsx +3 -3
- package/src/internal/useBuildSideEntityController.tsx +1 -1
- package/src/types/navigation.ts +1 -1
- package/src/util/navigation_utils.ts +6 -6
- package/dist/internal/useLocaleConfig.d.ts +0 -1
- package/src/internal/useLocaleConfig.tsx +0 -18
|
@@ -47,7 +47,7 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
|
|
|
47
47
|
* Get the collection configuration for a given path.
|
|
48
48
|
* The collection is resolved from the given path or alias.
|
|
49
49
|
*/
|
|
50
|
-
getCollection: (
|
|
50
|
+
getCollection: (pathOrId: string, entityId?: string, includeUserOverride?: boolean) => EC | undefined;
|
|
51
51
|
/**
|
|
52
52
|
* Get the collection configuration from its parent path segments.
|
|
53
53
|
*/
|
|
@@ -8,10 +8,10 @@ export declare function resolveCollectionPathIds(path: string, allCollections: E
|
|
|
8
8
|
/**
|
|
9
9
|
* Find the corresponding view at any depth for a given path.
|
|
10
10
|
* Note that path or segments of the paths can be collection aliases.
|
|
11
|
-
* @param
|
|
11
|
+
* @param pathOrId
|
|
12
12
|
* @param collections
|
|
13
13
|
*/
|
|
14
|
-
export declare function getCollectionByPathOrId(
|
|
14
|
+
export declare function getCollectionByPathOrId(pathOrId: string, collections: EntityCollection[]): EntityCollection | undefined;
|
|
15
15
|
/**
|
|
16
16
|
* Get the subcollection combinations from a path:
|
|
17
17
|
* "sites/es/locales" => ["sites/es/locales", "sites"]
|
|
@@ -2,11 +2,11 @@ import { ArrayProperty, CMSType, EntityCollection, EntityCustomView, EntityValue
|
|
|
2
2
|
export declare const resolveCollection: <M extends Record<string, any>>({ collection, path, entityId, values, previousValues, userConfigPersistence, fields }: {
|
|
3
3
|
collection: EntityCollection<M> | ResolvedEntityCollection<M>;
|
|
4
4
|
path: string;
|
|
5
|
-
entityId?: string
|
|
6
|
-
values?: Partial<M
|
|
7
|
-
previousValues?: Partial<M
|
|
8
|
-
userConfigPersistence?: UserConfigurationPersistence
|
|
9
|
-
fields?: Record<string, PropertyConfig
|
|
5
|
+
entityId?: string;
|
|
6
|
+
values?: Partial<EntityValues<M>>;
|
|
7
|
+
previousValues?: Partial<EntityValues<M>>;
|
|
8
|
+
userConfigPersistence?: UserConfigurationPersistence;
|
|
9
|
+
fields?: Record<string, PropertyConfig>;
|
|
10
10
|
}) => ResolvedEntityCollection<M>;
|
|
11
11
|
/**
|
|
12
12
|
* Resolve property builders, enums and arrays.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.0-canary.
|
|
4
|
+
"version": "3.0.0-canary.41",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -46,17 +46,20 @@
|
|
|
46
46
|
"./package.json": "./package.json"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@firecms/formex": "^3.0.0-canary.
|
|
50
|
-
"@firecms/ui": "^3.0.0-canary.
|
|
49
|
+
"@firecms/formex": "^3.0.0-canary.41",
|
|
50
|
+
"@firecms/ui": "^3.0.0-canary.41",
|
|
51
51
|
"@fontsource/jetbrains-mono": "^5.0.19",
|
|
52
|
-
"@fontsource/roboto": "^5.0.12",
|
|
53
52
|
"@hello-pangea/dnd": "^16.5.0",
|
|
53
|
+
"@radix-ui/react-portal": "^1.0.4",
|
|
54
|
+
"clsx": "^2.1.0",
|
|
54
55
|
"date-fns": "^3.6.0",
|
|
55
56
|
"history": "^5.3.0",
|
|
56
57
|
"js-search": "^2.0.1",
|
|
57
58
|
"markdown-it": "^14.1.0",
|
|
58
59
|
"notistack": "^3.0.1",
|
|
59
60
|
"object-hash": "^3.0.0",
|
|
61
|
+
"react-dropzone": "^14.2.3",
|
|
62
|
+
"react-fast-compare": "^3.2.2",
|
|
60
63
|
"react-image-file-resizer": "^0.4.8",
|
|
61
64
|
"react-markdown-editor-lite": "^1.3.4",
|
|
62
65
|
"react-transition-group": "^4.4.5",
|
|
@@ -115,7 +118,7 @@
|
|
|
115
118
|
"dist",
|
|
116
119
|
"src"
|
|
117
120
|
],
|
|
118
|
-
"gitHead": "
|
|
121
|
+
"gitHead": "d1ffa185f8930bab4e7b9941ba551c53f947aa1f",
|
|
119
122
|
"publishConfig": {
|
|
120
123
|
"access": "public"
|
|
121
124
|
}
|
|
@@ -74,7 +74,6 @@ import { DeleteEntityDialog } from "../DeleteEntityDialog";
|
|
|
74
74
|
import { useAnalyticsController } from "../../hooks/useAnalyticsController";
|
|
75
75
|
import { useSelectionController } from "./useSelectionController";
|
|
76
76
|
import { EntityCollectionViewStartActions } from "./EntityCollectionViewStartActions";
|
|
77
|
-
import { ClearFilterSortButton } from "../ClearFilterSortButton";
|
|
78
77
|
|
|
79
78
|
const COLLECTION_GROUP_PARENT_ID = "collectionGroupParent";
|
|
80
79
|
|
|
@@ -82,8 +81,18 @@ const COLLECTION_GROUP_PARENT_ID = "collectionGroupParent";
|
|
|
82
81
|
* @group Components
|
|
83
82
|
*/
|
|
84
83
|
export type EntityCollectionViewProps<M extends Record<string, any>> = {
|
|
85
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Complete path where this collection is located.
|
|
86
|
+
* It defaults to the collection path if not provided.
|
|
87
|
+
*/
|
|
88
|
+
fullPath?: string;
|
|
89
|
+
/**
|
|
90
|
+
* If this is a subcollection, specify the parent collection ids.
|
|
91
|
+
*/
|
|
86
92
|
parentCollectionIds?: string[];
|
|
93
|
+
/**
|
|
94
|
+
* Whether this is a subcollection or not.
|
|
95
|
+
*/
|
|
87
96
|
isSubCollection?: boolean;
|
|
88
97
|
className?: string;
|
|
89
98
|
} & EntityCollection<M>;
|
|
@@ -114,7 +123,7 @@ export type EntityCollectionViewProps<M extends Record<string, any>> = {
|
|
|
114
123
|
*/
|
|
115
124
|
export const EntityCollectionView = React.memo(
|
|
116
125
|
function EntityCollectionView<M extends Record<string, any>>({
|
|
117
|
-
fullPath,
|
|
126
|
+
fullPath: fullPathProp,
|
|
118
127
|
parentCollectionIds,
|
|
119
128
|
isSubCollection,
|
|
120
129
|
className,
|
|
@@ -122,6 +131,7 @@ export const EntityCollectionView = React.memo(
|
|
|
122
131
|
}: EntityCollectionViewProps<M>
|
|
123
132
|
) {
|
|
124
133
|
|
|
134
|
+
const fullPath = fullPathProp ?? collectionProp.path;
|
|
125
135
|
const dataSource = useDataSource(collectionProp);
|
|
126
136
|
const navigation = useNavigationController();
|
|
127
137
|
const sideEntityController = useSideEntityController();
|
|
@@ -176,7 +186,6 @@ export const EntityCollectionView = React.memo(
|
|
|
176
186
|
const usedSelectionController = collection.selectionController ?? selectionController;
|
|
177
187
|
const {
|
|
178
188
|
selectedEntities,
|
|
179
|
-
toggleEntitySelection,
|
|
180
189
|
isEntitySelected,
|
|
181
190
|
setSelectedEntities
|
|
182
191
|
} = usedSelectionController;
|
|
@@ -58,7 +58,7 @@ export function EntityView<M extends Record<string, any>>(
|
|
|
58
58
|
</div>
|
|
59
59
|
{Object.entries(properties)
|
|
60
60
|
.map(([key, property]) => {
|
|
61
|
-
const value =
|
|
61
|
+
const value = entity.values?.[key];
|
|
62
62
|
return (
|
|
63
63
|
<div
|
|
64
64
|
key={`reference_previews_${key}`}
|
|
@@ -20,7 +20,7 @@ import { VirtualTableContextProps } from "./types";
|
|
|
20
20
|
import { VirtualTableHeaderRow } from "./VirtualTableHeaderRow";
|
|
21
21
|
import { VirtualTableRow } from "./VirtualTableRow";
|
|
22
22
|
import { VirtualTableCell } from "./VirtualTableCell";
|
|
23
|
-
import { AssignmentIcon, cn,
|
|
23
|
+
import { AssignmentIcon, cn, Typography } from "@firecms/ui";
|
|
24
24
|
|
|
25
25
|
const VirtualListContext = createContext<VirtualTableContextProps<any>>({} as any);
|
|
26
26
|
VirtualListContext.displayName = "VirtualListContext";
|
|
@@ -225,11 +225,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
if (onFilterUpdate) onFilterUpdate(newFilterValue);
|
|
228
|
-
|
|
229
|
-
if (column.key !== sortByProperty) {
|
|
230
|
-
resetSort();
|
|
231
|
-
}
|
|
232
|
-
}, [checkFilterCombination, currentSort, onFilterUpdate, resetSort, sortByProperty]);
|
|
228
|
+
}, [checkFilterCombination, currentSort, onFilterUpdate, sortByProperty]);
|
|
233
229
|
|
|
234
230
|
const buildErrorView = useCallback(() => (
|
|
235
231
|
<div
|
|
@@ -239,7 +235,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
239
235
|
{"Error fetching data from the data source"}
|
|
240
236
|
</Typography>
|
|
241
237
|
|
|
242
|
-
{error?.message && <
|
|
238
|
+
{error?.message && <SafeLinkRenderer text={error.message}/>}
|
|
243
239
|
|
|
244
240
|
</div>
|
|
245
241
|
), [error?.message]);
|
|
@@ -403,3 +399,17 @@ function MemoizedList({
|
|
|
403
399
|
{Row}
|
|
404
400
|
</List>;
|
|
405
401
|
}
|
|
402
|
+
|
|
403
|
+
const SafeLinkRenderer: React.FC<{
|
|
404
|
+
text: string;
|
|
405
|
+
}> = ({ text }) => {
|
|
406
|
+
const urlRegex = /https?:\/\/[^\s]+/g;
|
|
407
|
+
const htmlContent = text.replace(urlRegex, (url) => {
|
|
408
|
+
// For each URL found, replace it with an HTML <a> tag
|
|
409
|
+
return `<a href="${url}" target="_blank">Link to your console</a>`;
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
return (
|
|
413
|
+
<div className={"px-4 break-all"} dangerouslySetInnerHTML={{ __html: htmlContent }}/>
|
|
414
|
+
);
|
|
415
|
+
};
|
package/src/core/FireCMS.tsx
CHANGED
|
@@ -12,8 +12,7 @@ import { DataSourceContext } from "../contexts/DataSourceContext";
|
|
|
12
12
|
import { SideEntityControllerContext } from "../contexts/SideEntityControllerContext";
|
|
13
13
|
import { NavigationContext } from "../contexts/NavigationContext";
|
|
14
14
|
import { SideDialogsControllerContext } from "../contexts/SideDialogsControllerContext";
|
|
15
|
-
import { useLocaleConfig } from "
|
|
16
|
-
import { CenteredView, Typography } from "@firecms/ui";
|
|
15
|
+
import { CenteredView, Typography, useLocaleConfig } from "@firecms/ui";
|
|
17
16
|
import { DialogsProvider } from "../contexts/DialogsProvider";
|
|
18
17
|
import { useBuildDataSource } from "../internal/useBuildDataSource";
|
|
19
18
|
import { useBuildCustomizationController } from "../internal/useBuildCustomizationController";
|
|
@@ -276,9 +276,9 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
276
276
|
const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
|
|
277
277
|
|
|
278
278
|
const collections = collectionsRef.current;
|
|
279
|
+
if (collections === undefined)
|
|
280
|
+
throw Error("getCollectionFromPaths: Collections have not been initialised yet");
|
|
279
281
|
let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
|
|
280
|
-
if (!currentCollections)
|
|
281
|
-
throw Error("Collections have not been initialised yet");
|
|
282
282
|
|
|
283
283
|
for (let i = 0; i < pathSegments.length; i++) {
|
|
284
284
|
const pathSegment = pathSegments[i];
|
|
@@ -297,9 +297,9 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
297
297
|
const getCollectionFromIds = useCallback(<EC extends EntityCollection>(ids: string[]): EC | undefined => {
|
|
298
298
|
|
|
299
299
|
const collections = collectionsRef.current;
|
|
300
|
+
if (collections === undefined)
|
|
301
|
+
throw Error("getCollectionFromIds: Collections have not been initialised yet");
|
|
300
302
|
let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
|
|
301
|
-
if (!currentCollections)
|
|
302
|
-
throw Error("Collections have not been initialised yet");
|
|
303
303
|
|
|
304
304
|
for (let i = 0; i < ids.length; i++) {
|
|
305
305
|
const id = ids[i];
|
|
@@ -335,9 +335,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
335
335
|
[]);
|
|
336
336
|
|
|
337
337
|
const resolveAliasesFrom = useCallback((path: string): string => {
|
|
338
|
-
const collections = collectionsRef.current;
|
|
339
|
-
if (!collections)
|
|
340
|
-
throw Error("Collections have not been initialised yet");
|
|
338
|
+
const collections = collectionsRef.current ?? [];
|
|
341
339
|
return resolveCollectionPathIds(path, collections);
|
|
342
340
|
}, []);
|
|
343
341
|
|
|
@@ -440,7 +438,7 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
|
|
|
440
438
|
collectionPermissions: PermissionsBuilder | undefined,
|
|
441
439
|
authController: AuthController,
|
|
442
440
|
dataSource: DataSourceDelegate,
|
|
443
|
-
injectCollections?: (collections: EntityCollection[]) => EntityCollection[]) {
|
|
441
|
+
injectCollections?: (collections: EntityCollection[]) => EntityCollection[]): Promise<EntityCollection[]> {
|
|
444
442
|
let resolvedCollections: EntityCollection[] = [];
|
|
445
443
|
if (typeof collections === "function") {
|
|
446
444
|
resolvedCollections = await collections({
|
|
@@ -489,8 +487,10 @@ function areCollectionListsEqual(a: EntityCollection[], b: EntityCollection[]) {
|
|
|
489
487
|
if (a.length !== b.length) {
|
|
490
488
|
return false;
|
|
491
489
|
}
|
|
492
|
-
const
|
|
493
|
-
const
|
|
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
494
|
return aSorted.every((value, index) => areCollectionsEqual(value, bSorted[index]));
|
|
495
495
|
}
|
|
496
496
|
|
|
@@ -9,7 +9,7 @@ export type AccessResponse = {
|
|
|
9
9
|
message?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
async function makeRequest(authController: AuthController, pluginKeys: string | undefined) {
|
|
12
|
+
async function makeRequest(authController: AuthController, pluginKeys: string[] | undefined) {
|
|
13
13
|
const firebaseToken = await authController.getAuthToken();
|
|
14
14
|
return fetch(DEFAULT_SERVER + "/access_log",
|
|
15
15
|
{
|
|
@@ -30,12 +30,12 @@ export function useProjectLog(authController: AuthController,
|
|
|
30
30
|
plugins?: FireCMSPlugin<any, any, any>[]): AccessResponse | null {
|
|
31
31
|
const [accessResponse, setAccessResponse] = useState<AccessResponse | null>(null);
|
|
32
32
|
const accessedUserRef = useRef<string | null>(null);
|
|
33
|
-
const pluginKeys = plugins?.map(plugin => plugin.key)
|
|
33
|
+
const pluginKeys = plugins?.map(plugin => plugin.key);
|
|
34
34
|
useEffect(() => {
|
|
35
35
|
if (authController.user && authController.user.uid !== accessedUserRef.current && !authController.initialLoading) {
|
|
36
36
|
makeRequest(authController, pluginKeys).then(setAccessResponse);
|
|
37
37
|
accessedUserRef.current = authController.user.uid;
|
|
38
38
|
}
|
|
39
|
-
}, [authController]);
|
|
39
|
+
}, [authController, pluginKeys]);
|
|
40
40
|
return accessResponse;
|
|
41
41
|
}
|
|
@@ -49,7 +49,7 @@ export const useBuildSideEntityController = (navigation: NavigationController,
|
|
|
49
49
|
}, 1);
|
|
50
50
|
}
|
|
51
51
|
} else {
|
|
52
|
-
console.warn("Location path is not a collection path");
|
|
52
|
+
// console.warn("Location path is not a collection path");
|
|
53
53
|
}
|
|
54
54
|
initialised.current = true;
|
|
55
55
|
}
|
package/src/types/navigation.ts
CHANGED
|
@@ -56,7 +56,7 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
|
|
|
56
56
|
* Get the collection configuration for a given path.
|
|
57
57
|
* The collection is resolved from the given path or alias.
|
|
58
58
|
*/
|
|
59
|
-
getCollection: (
|
|
59
|
+
getCollection: (pathOrId: string,
|
|
60
60
|
entityId?: string,
|
|
61
61
|
includeUserOverride?: boolean) => EC | undefined;
|
|
62
62
|
/**
|
|
@@ -60,14 +60,14 @@ export function resolveCollectionPathIds(path: string, allCollections: EntityCol
|
|
|
60
60
|
/**
|
|
61
61
|
* Find the corresponding view at any depth for a given path.
|
|
62
62
|
* Note that path or segments of the paths can be collection aliases.
|
|
63
|
-
* @param
|
|
63
|
+
* @param pathOrId
|
|
64
64
|
* @param collections
|
|
65
65
|
*/
|
|
66
|
-
export function getCollectionByPathOrId(
|
|
66
|
+
export function getCollectionByPathOrId(pathOrId: string, collections: EntityCollection[]): EntityCollection | undefined {
|
|
67
67
|
|
|
68
|
-
const subpaths = removeInitialAndTrailingSlashes(
|
|
68
|
+
const subpaths = removeInitialAndTrailingSlashes(pathOrId).split("/");
|
|
69
69
|
if (subpaths.length % 2 === 0) {
|
|
70
|
-
throw Error(`
|
|
70
|
+
throw Error(`getCollectionByPathOrId: Collection paths must have an odd number of segments: ${pathOrId}`);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
const subpathCombinations = getCollectionPathsCombinations(subpaths);
|
|
@@ -80,10 +80,10 @@ export function getCollectionByPathOrId(pathOrAlias: string, collections: Entity
|
|
|
80
80
|
|
|
81
81
|
if (navigationEntry) {
|
|
82
82
|
|
|
83
|
-
if (subpathCombination ===
|
|
83
|
+
if (subpathCombination === pathOrId) {
|
|
84
84
|
result = navigationEntry;
|
|
85
85
|
} else if (navigationEntry.subcollections) {
|
|
86
|
-
const newPath =
|
|
86
|
+
const newPath = pathOrId.replace(subpathCombination, "").split("/").slice(2).join("/");
|
|
87
87
|
if (newPath.length > 0)
|
|
88
88
|
result = getCollectionByPathOrId(newPath, navigationEntry.subcollections);
|
|
89
89
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function useLocaleConfig(locale?: string): void;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import * as locales from "date-fns/locale";
|
|
2
|
-
// @ts-ignore
|
|
3
|
-
import { registerLocale, setDefaultLocale } from "react-datepicker";
|
|
4
|
-
import { useEffect } from "react";
|
|
5
|
-
|
|
6
|
-
export function useLocaleConfig(locale?: string) {
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
if (!locale) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
// @ts-ignore
|
|
12
|
-
const dateFnsLocale = locales[locale];
|
|
13
|
-
if (dateFnsLocale) {
|
|
14
|
-
registerLocale(locale, dateFnsLocale);
|
|
15
|
-
setDefaultLocale(locale);
|
|
16
|
-
}
|
|
17
|
-
}, [locale])
|
|
18
|
-
}
|