@firecms/core 3.0.0-rc.2 → 3.0.0-rc.4
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/HomePage/HomePageDnD.d.ts +2 -1
- package/dist/components/PropertyCollectionView.d.ts +23 -0
- package/dist/core/EntityEditView.d.ts +10 -4
- package/dist/form/EntityForm.d.ts +5 -2
- package/dist/form/PropertyFieldBinding.d.ts +1 -1
- package/dist/form/components/LocalChangesMenu.d.ts +11 -0
- package/dist/form/index.d.ts +2 -1
- package/dist/index.es.js +1307 -384
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1306 -383
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collections.d.ts +11 -0
- package/dist/types/fields.d.ts +8 -0
- package/dist/types/properties.d.ts +32 -6
- package/dist/util/collections.d.ts +1 -0
- package/dist/util/entity_cache.d.ts +6 -1
- package/dist/util/make_properties_editable.d.ts +1 -2
- package/dist/util/objects.d.ts +1 -0
- package/dist/util/useStorageUploadController.d.ts +1 -0
- package/package.json +6 -6
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +47 -47
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +6 -1
- package/src/components/EntityView.tsx +29 -40
- package/src/components/HomePage/DefaultHomePage.tsx +13 -9
- package/src/components/HomePage/HomePageDnD.tsx +140 -38
- package/src/components/PropertyCollectionView.tsx +329 -0
- package/src/components/SelectableTable/SelectableTable.tsx +0 -12
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +2 -1
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +0 -1
- package/src/core/EntityEditView.tsx +27 -14
- package/src/core/EntityEditViewFormActions.tsx +33 -18
- package/src/core/EntitySidePanel.tsx +9 -3
- package/src/form/EntityForm.tsx +173 -42
- package/src/form/EntityFormActions.tsx +30 -15
- package/src/form/PropertyFieldBinding.tsx +4 -4
- package/src/form/components/ErrorFocus.tsx +22 -29
- package/src/form/components/LocalChangesMenu.tsx +144 -0
- package/src/form/field_bindings/BlockFieldBinding.tsx +1 -0
- package/src/form/index.tsx +5 -1
- package/src/hooks/useBuildNavigationController.tsx +104 -31
- package/src/preview/property_previews/MapPropertyPreview.tsx +2 -2
- package/src/preview/property_previews/NumberPropertyPreview.tsx +2 -2
- package/src/types/collections.ts +12 -0
- package/src/types/fields.tsx +10 -0
- package/src/types/properties.ts +35 -6
- package/src/util/collections.ts +8 -0
- package/src/util/createFormexStub.tsx +4 -0
- package/src/util/entity_cache.ts +71 -52
- package/src/util/join_collections.ts +3 -3
- package/src/util/make_properties_editable.ts +0 -22
- package/src/util/objects.ts +40 -2
- package/src/util/useStorageUploadController.tsx +71 -34
|
@@ -307,6 +307,17 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
307
307
|
* This prop has no effect if the history plugin is not enabled
|
|
308
308
|
*/
|
|
309
309
|
history?: boolean;
|
|
310
|
+
/**
|
|
311
|
+
* Should local changes be backed up in local storage, to prevent data loss on
|
|
312
|
+
* accidental navigations.
|
|
313
|
+
* - `manual_apply`: When the user navigates back to an entity with local changes,
|
|
314
|
+
* they will be prompted to restore the changes.
|
|
315
|
+
* - `auto_apply`: When the user navigates back to an entity with local changes,
|
|
316
|
+
* the changes will be automatically applied.
|
|
317
|
+
* - `false`: Local changes will not be backed up.
|
|
318
|
+
* Defaults to `manual_apply`.
|
|
319
|
+
*/
|
|
320
|
+
localChangesBackup?: "manual_apply" | "auto_apply" | false;
|
|
310
321
|
}
|
|
311
322
|
/**
|
|
312
323
|
* Parameter passed to the `Actions` prop in the collection configuration.
|
package/dist/types/fields.d.ts
CHANGED
|
@@ -67,6 +67,10 @@ export interface FieldProps<T extends CMSType = any, CustomProps = any, M extend
|
|
|
67
67
|
* Is this field part of an array
|
|
68
68
|
*/
|
|
69
69
|
partOfArray?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Is this field part of a block
|
|
72
|
+
*/
|
|
73
|
+
partOfBlock?: boolean;
|
|
70
74
|
/**
|
|
71
75
|
* Display the child properties directly, without being wrapped in an
|
|
72
76
|
* extendable panel. Note that this will also hide the title of this property.
|
|
@@ -178,6 +182,10 @@ export interface PropertyFieldBindingProps<T extends CMSType, M extends Record<s
|
|
|
178
182
|
* Is this field part of an array
|
|
179
183
|
*/
|
|
180
184
|
partOfArray?: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* Is this field part of a block
|
|
187
|
+
*/
|
|
188
|
+
partOfBlock?: boolean;
|
|
181
189
|
/**
|
|
182
190
|
* Display the child properties directly, without being wrapped in an
|
|
183
191
|
* extendable panel. Note that this will also hide the title of this property.
|
|
@@ -102,6 +102,10 @@ export interface BaseProperty<T extends CMSType, CustomProps = any> {
|
|
|
102
102
|
/**
|
|
103
103
|
* Should this property be editable. If set to true, the user will be able to modify the property and
|
|
104
104
|
* save the new config. The saved config will then become the source of truth.
|
|
105
|
+
* Defaults to `true.
|
|
106
|
+
* This props is only useful when you are using the collection editor to modify collection
|
|
107
|
+
* configurations from the CMS itself. You can also use the `editable` prop in the
|
|
108
|
+
* `EntityCollection` interface to disable the edition of all properties in a collection.
|
|
105
109
|
*/
|
|
106
110
|
editable?: boolean;
|
|
107
111
|
/**
|
|
@@ -617,8 +621,15 @@ export type StorageConfig = {
|
|
|
617
621
|
/**
|
|
618
622
|
* Use client side image compression and resizing
|
|
619
623
|
* Will only be applied to these MIME types: image/jpeg, image/png and image/webp
|
|
624
|
+
* @deprecated Use `imageResize` instead
|
|
620
625
|
*/
|
|
621
|
-
imageCompression?:
|
|
626
|
+
imageCompression?: ImageResize;
|
|
627
|
+
/**
|
|
628
|
+
* Advanced image resizing and cropping configuration.
|
|
629
|
+
* Applied before upload to optimize storage and bandwidth.
|
|
630
|
+
* Only applies to image MIME types: image/jpeg, image/png, image/webp
|
|
631
|
+
*/
|
|
632
|
+
imageResize?: ImageResize;
|
|
622
633
|
/**
|
|
623
634
|
* Specific metadata set in your uploaded file.
|
|
624
635
|
* For the default Firebase implementation, the values passed here are of type
|
|
@@ -730,17 +741,32 @@ export type PreviewType = "image" | "video" | "audio" | "file";
|
|
|
730
741
|
* @group Entity properties
|
|
731
742
|
*/
|
|
732
743
|
export type FileType = "image/*" | "video/*" | "audio/*" | "application/*" | "text/*" | "font/*" | string;
|
|
733
|
-
export interface
|
|
744
|
+
export interface ImageResize {
|
|
745
|
+
/**
|
|
746
|
+
* Maximum width in pixels. Image will be scaled down proportionally if wider.
|
|
747
|
+
*/
|
|
748
|
+
maxWidth?: number;
|
|
734
749
|
/**
|
|
735
|
-
*
|
|
750
|
+
* Maximum height in pixels. Image will be scaled down proportionally if taller.
|
|
736
751
|
*/
|
|
737
752
|
maxHeight?: number;
|
|
738
753
|
/**
|
|
739
|
-
*
|
|
754
|
+
* Resize mode determines how the image fits within maxWidth/maxHeight bounds.
|
|
755
|
+
* - `contain`: Scale down to fit within bounds, preserving aspect ratio (default)
|
|
756
|
+
* - `cover`: Scale to fill bounds, preserving aspect ratio (may crop)
|
|
740
757
|
*/
|
|
741
|
-
|
|
758
|
+
mode?: 'contain' | 'cover';
|
|
759
|
+
/**
|
|
760
|
+
* Output format for the resized image.
|
|
761
|
+
* - `original`: Keep the original format (default)
|
|
762
|
+
* - `jpeg`: Convert to JPEG
|
|
763
|
+
* - `png`: Convert to PNG
|
|
764
|
+
* - `webp`: Convert to WebP
|
|
765
|
+
*/
|
|
766
|
+
format?: 'original' | 'jpeg' | 'png' | 'webp';
|
|
742
767
|
/**
|
|
743
|
-
*
|
|
768
|
+
* Quality for lossy formats (JPEG, WebP). Number between 0 and 100.
|
|
769
|
+
* Higher is better quality but larger file size. Defaults to 80.
|
|
744
770
|
*/
|
|
745
771
|
quality?: number;
|
|
746
772
|
}
|
|
@@ -9,3 +9,4 @@ export declare function resolveDefaultSelectedView(defaultSelectedView: string |
|
|
|
9
9
|
* @param permissionsBuilder
|
|
10
10
|
*/
|
|
11
11
|
export declare const applyPermissionsFunctionIfEmpty: (collections: EntityCollection[], permissionsBuilder?: PermissionsBuilder<any, any>) => EntityCollection[];
|
|
12
|
+
export declare function getLocalChangesBackup(collection: EntityCollection): "manual_apply" | "auto_apply";
|
|
@@ -4,14 +4,18 @@
|
|
|
4
4
|
* @param data - The data to cache and persist.
|
|
5
5
|
*/
|
|
6
6
|
export declare function saveEntityToCache(path: string, data: object): void;
|
|
7
|
+
export declare function removeEntityFromMemoryCache(path: string): void;
|
|
8
|
+
export declare function saveEntityToMemoryCache(path: string, data: object): void;
|
|
9
|
+
export declare function getEntityFromMemoryCache(path: string): object | undefined;
|
|
10
|
+
export declare function hasEntityInCache(path: string): boolean;
|
|
7
11
|
/**
|
|
8
12
|
* Retrieves an entity from the in-memory cache or `localStorage`.
|
|
9
13
|
* If the entity is not in the cache but exists in `localStorage`, it loads it into the cache.
|
|
10
14
|
* @param path - The unique path/key for the entity.
|
|
15
|
+
* @param useLocalStorage
|
|
11
16
|
* @returns The cached entity or `undefined` if not found.
|
|
12
17
|
*/
|
|
13
18
|
export declare function getEntityFromCache(path: string): object | undefined;
|
|
14
|
-
export declare function hasEntityInCache(path: string): boolean;
|
|
15
19
|
/**
|
|
16
20
|
* Removes an entity from both the in-memory cache and `localStorage`.
|
|
17
21
|
* @param path - The unique path/key for the entity to remove.
|
|
@@ -21,3 +25,4 @@ export declare function removeEntityFromCache(path: string): void;
|
|
|
21
25
|
* Clears the entire in-memory cache and removes all related entities from `localStorage`.
|
|
22
26
|
*/
|
|
23
27
|
export declare function clearEntityCache(): void;
|
|
28
|
+
export declare function flattenKeys(obj: any, prefix?: string, result?: string[]): string[];
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import { Properties
|
|
1
|
+
import { Properties } from "../types";
|
|
2
2
|
export declare function makePropertiesEditable(properties: Properties): Properties<any>;
|
|
3
|
-
export declare function makePropertiesNonEditable(properties: PropertiesOrBuilders): PropertiesOrBuilders;
|
package/dist/util/objects.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const pick: <T>(obj: T, ...args: any[]) => T;
|
|
2
2
|
export declare function isObject(item: any): any;
|
|
3
|
+
export declare function isPlainObject(obj: any): boolean;
|
|
3
4
|
export declare function mergeDeep<T extends Record<any, any>, U extends Record<any, any>>(target: T, source: U, ignoreUndefined?: boolean): T & U;
|
|
4
5
|
export declare function getValueInPath(o: object | undefined, path: string): any;
|
|
5
6
|
export declare function removeInPath(o: object, path: string): object | undefined;
|
|
@@ -31,6 +31,7 @@ export declare function useStorageUploadController<M extends object>({ entityId,
|
|
|
31
31
|
fileNameBuilder: (file: File) => Promise<string>;
|
|
32
32
|
storagePathBuilder: (file: File) => string;
|
|
33
33
|
onFileUploadComplete: (uploadedPath: string, entry: StorageFieldItem, metadata?: any) => Promise<void>;
|
|
34
|
+
onFileUploadError: (entry: StorageFieldItem) => void;
|
|
34
35
|
onFilesAdded: (acceptedFiles: File[]) => Promise<void>;
|
|
35
36
|
multipleFilesSupported: boolean;
|
|
36
37
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.0-rc.
|
|
4
|
+
"version": "3.0.0-rc.4",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -53,11 +53,12 @@
|
|
|
53
53
|
"@dnd-kit/core": "^6.3.1",
|
|
54
54
|
"@dnd-kit/modifiers": "^9.0.0",
|
|
55
55
|
"@dnd-kit/sortable": "^10.0.0",
|
|
56
|
-
"@firecms/editor": "^3.0.0-rc.
|
|
57
|
-
"@firecms/formex": "^3.0.0-rc.
|
|
58
|
-
"@firecms/ui": "^3.0.0-rc.
|
|
56
|
+
"@firecms/editor": "^3.0.0-rc.4",
|
|
57
|
+
"@firecms/formex": "^3.0.0-rc.4",
|
|
58
|
+
"@firecms/ui": "^3.0.0-rc.4",
|
|
59
59
|
"@radix-ui/react-portal": "^1.1.9",
|
|
60
60
|
"clsx": "^2.1.1",
|
|
61
|
+
"compressorjs": "^1.2.1",
|
|
61
62
|
"date-fns": "^3.6.0",
|
|
62
63
|
"fuse.js": "^7.1.0",
|
|
63
64
|
"history": "^5.3.0",
|
|
@@ -67,7 +68,6 @@
|
|
|
67
68
|
"prism-react-renderer": "^2.4.1",
|
|
68
69
|
"react-dropzone": "^14.3.8",
|
|
69
70
|
"react-fast-compare": "^3.2.2",
|
|
70
|
-
"react-image-file-resizer": "^0.4.8",
|
|
71
71
|
"react-transition-group": "^4.4.5",
|
|
72
72
|
"react-use-measure": "^2.1.7",
|
|
73
73
|
"react-window": "^1.8.11",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"dist",
|
|
109
109
|
"src"
|
|
110
110
|
],
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "5fab14c8d1fff7dd606512b5d9b3f1842e601f8c",
|
|
112
112
|
"publishConfig": {
|
|
113
113
|
"access": "public"
|
|
114
114
|
},
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
import React, { MouseEvent, useCallback } from "react";
|
|
2
2
|
|
|
3
3
|
import { CollectionSize, Entity, EntityAction, EntityCollection, SelectionController } from "../../types";
|
|
4
|
-
import {
|
|
5
|
-
Checkbox,
|
|
6
|
-
Chip,
|
|
7
|
-
cls,
|
|
8
|
-
EditIcon,
|
|
9
|
-
IconButton,
|
|
10
|
-
Menu,
|
|
11
|
-
MenuItem,
|
|
12
|
-
MoreVertIcon,
|
|
13
|
-
Skeleton,
|
|
14
|
-
Tooltip
|
|
15
|
-
} from "@firecms/ui";
|
|
4
|
+
import { Badge, Checkbox, cls, IconButton, Menu, MenuItem, MoreVertIcon, Skeleton, Tooltip } from "@firecms/ui";
|
|
16
5
|
import { useFireCMSContext, useLargeLayout } from "../../hooks";
|
|
17
|
-
import {
|
|
6
|
+
import { getEntityFromCache } from "../../util/entity_cache";
|
|
7
|
+
import { getLocalChangesBackup } from "../../util";
|
|
18
8
|
|
|
19
9
|
/**
|
|
20
10
|
*
|
|
@@ -79,7 +69,9 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
|
|
|
79
69
|
|
|
80
70
|
const collapsedActions = actions.filter(a => a.collapsed || a.collapsed === undefined);
|
|
81
71
|
const uncollapsedActions = actions.filter(a => a.collapsed === false);
|
|
82
|
-
const
|
|
72
|
+
const enableLocalChangesBackup = collection ? getLocalChangesBackup(collection) : false;
|
|
73
|
+
const hasDraft = enableLocalChangesBackup ? getEntityFromCache(fullPath + "/" + entity.id) : false;
|
|
74
|
+
const iconSize = largeLayout && (size === "m" || size === "l" || size == "xl") ? "medium" : "small";
|
|
83
75
|
return (
|
|
84
76
|
<div
|
|
85
77
|
className={cls(
|
|
@@ -99,37 +91,50 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
|
|
|
99
91
|
{(hasActions || selectionEnabled) &&
|
|
100
92
|
<div className="w-34 flex justify-center">
|
|
101
93
|
|
|
102
|
-
{uncollapsedActions.map((action, index) =>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
94
|
+
{uncollapsedActions.map((action, index) => {
|
|
95
|
+
const isEditAction = action.key === "edit";
|
|
96
|
+
const tooltip = isEditAction && hasDraft ? "Local unsaved changes" : action.name;
|
|
97
|
+
|
|
98
|
+
let iconButton = <IconButton
|
|
99
|
+
onClick={(event: MouseEvent) => {
|
|
100
|
+
event.stopPropagation();
|
|
101
|
+
action.onClick({
|
|
102
|
+
view: "collection",
|
|
103
|
+
entity,
|
|
104
|
+
fullPath,
|
|
105
|
+
fullIdPath,
|
|
106
|
+
collection,
|
|
107
|
+
context,
|
|
108
|
+
selectionController,
|
|
109
|
+
highlightEntity,
|
|
110
|
+
unhighlightEntity,
|
|
111
|
+
onCollectionChange,
|
|
112
|
+
openEntityMode: openEntityMode ?? collection?.openEntityMode
|
|
113
|
+
});
|
|
114
|
+
}}
|
|
115
|
+
size={iconSize}>
|
|
116
|
+
{action.icon}
|
|
117
|
+
</IconButton>;
|
|
118
|
+
if (isEditAction && hasDraft) {
|
|
119
|
+
iconButton = (
|
|
120
|
+
<Badge color={"warning"}>
|
|
121
|
+
{iconButton}
|
|
122
|
+
</Badge>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return (
|
|
126
|
+
<Tooltip key={index}
|
|
127
|
+
title={tooltip}
|
|
128
|
+
asChild={true}>
|
|
129
|
+
{iconButton}
|
|
130
|
+
</Tooltip>
|
|
131
|
+
);
|
|
132
|
+
})}
|
|
128
133
|
|
|
129
134
|
{hasCollapsedActions &&
|
|
130
135
|
<Menu
|
|
131
136
|
trigger={<IconButton
|
|
132
|
-
size={
|
|
137
|
+
size={iconSize}>
|
|
133
138
|
<MoreVertIcon/>
|
|
134
139
|
</IconButton>}>
|
|
135
140
|
{collapsedActions.map((action, index) => (
|
|
@@ -161,7 +166,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
|
|
|
161
166
|
{selectionEnabled &&
|
|
162
167
|
<Tooltip title={`Select ${entity.id}`}>
|
|
163
168
|
<Checkbox
|
|
164
|
-
size={
|
|
169
|
+
size={iconSize}
|
|
165
170
|
checked={Boolean(isSelected)}
|
|
166
171
|
onCheckedChange={onCheckedChange}
|
|
167
172
|
/>
|
|
@@ -175,11 +180,6 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
|
|
|
175
180
|
onClick={(event) => {
|
|
176
181
|
event.stopPropagation();
|
|
177
182
|
}}>
|
|
178
|
-
{hasDraft && <Tooltip title={"Local unsaved changes"} className={"inline"}>
|
|
179
|
-
<Chip colorScheme={"orangeDarker"} className={"p-0.5"}>
|
|
180
|
-
<EditIcon size={12}/>
|
|
181
|
-
</Chip>
|
|
182
|
-
</Tooltip>}
|
|
183
183
|
<span className="min-w-0 truncate text-center">
|
|
184
184
|
{entity
|
|
185
185
|
? entity.id
|
|
@@ -376,6 +376,11 @@ export const EntityCollectionView = React.memo(
|
|
|
376
376
|
console.error("Save failure");
|
|
377
377
|
console.error(e);
|
|
378
378
|
setError(e);
|
|
379
|
+
},
|
|
380
|
+
onPreSaveHookError: (e: Error) => {
|
|
381
|
+
console.error("Pre-save hook error");
|
|
382
|
+
console.error(e);
|
|
383
|
+
setError(e);
|
|
379
384
|
}
|
|
380
385
|
});
|
|
381
386
|
|
|
@@ -694,7 +699,7 @@ export const EntityCollectionView = React.memo(
|
|
|
694
699
|
className="mt-4"
|
|
695
700
|
>
|
|
696
701
|
<AddIcon/>
|
|
697
|
-
Create your first
|
|
702
|
+
Create your first entry
|
|
698
703
|
</Button>
|
|
699
704
|
</div>
|
|
700
705
|
: <Typography variant={"label"}>No results with the applied filter/sort</Typography>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
|
-
import { PropertyPreview } from "../preview";
|
|
3
2
|
import { Entity, EntityCollection, ResolvedEntityCollection, ResolvedProperties } from "../types";
|
|
4
3
|
import { resolveCollection } from "../util";
|
|
5
|
-
import { cls, defaultBorderMixin, IconButton, OpenInNewIcon } from "@firecms/ui";
|
|
4
|
+
import { cls, defaultBorderMixin, IconButton, OpenInNewIcon, Typography } from "@firecms/ui";
|
|
6
5
|
import { CustomizationController } from "../types/customization_controller";
|
|
7
6
|
import { useCustomizationController } from "../hooks/useCustomizationController";
|
|
8
7
|
import { useAuthController } from "../hooks";
|
|
8
|
+
import { PropertyCollectionView } from "./PropertyCollectionView";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @group Components
|
|
@@ -40,47 +40,36 @@ export function EntityView<M extends Record<string, any>>(
|
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
42
|
<div className={"w-full " + className}>
|
|
43
|
-
<div className={"w-full mb-4"}>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
<div className={"w-full mb-4 p-4"}>
|
|
44
|
+
|
|
45
|
+
<div className={`grid grid-cols-12 gap-x-4 py-4 items-start border-b ${defaultBorderMixin}`}>
|
|
46
|
+
<div className="col-span-4 pr-2">
|
|
47
|
+
<Typography variant="caption"
|
|
48
|
+
color={"secondary"}
|
|
49
|
+
component={"span"}
|
|
50
|
+
className="break-words">
|
|
51
|
+
Id
|
|
52
|
+
</Typography>
|
|
47
53
|
</div>
|
|
48
|
-
<div
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
<div className="col-span-8">
|
|
55
|
+
<div
|
|
56
|
+
className="flex-grow text-surface-900 dark:text-white flex items-center">
|
|
57
|
+
<span className="flex-grow mr-2">{entity.id}</span>
|
|
58
|
+
{customizationController?.entityLinkBuilder &&
|
|
59
|
+
<a href={customizationController.entityLinkBuilder({ entity })}
|
|
60
|
+
rel="noopener noreferrer"
|
|
61
|
+
target="_blank">
|
|
62
|
+
<IconButton>
|
|
63
|
+
<OpenInNewIcon
|
|
64
|
+
size={"small"}/>
|
|
65
|
+
</IconButton>
|
|
66
|
+
</a>}
|
|
67
|
+
</div>
|
|
60
68
|
</div>
|
|
61
69
|
</div>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<div
|
|
67
|
-
key={`reference_previews_${key}`}
|
|
68
|
-
className={cls(defaultBorderMixin, "flex justify-between py-2 border-b last:border-b-0")}>
|
|
69
|
-
<div className="flex items-center w-1/4">
|
|
70
|
-
<span className="pl-2 text-sm text-surface-600">{property.name}</span>
|
|
71
|
-
</div>
|
|
72
|
-
<div
|
|
73
|
-
className="flex-grow p-2 ml-2 w-3/4 text-surface-900 dark:text-white min-h-[56px] flex items-center">
|
|
74
|
-
<PropertyPreview
|
|
75
|
-
propertyKey={key}
|
|
76
|
-
value={value}
|
|
77
|
-
// entity={entity}
|
|
78
|
-
property={property}
|
|
79
|
-
size={"medium"}/>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
)
|
|
83
|
-
})}
|
|
70
|
+
|
|
71
|
+
<PropertyCollectionView data={entity.values} properties={properties} size={"medium"}/>
|
|
72
|
+
|
|
84
73
|
</div>
|
|
85
74
|
</div>
|
|
86
75
|
);
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import Fuse from "fuse.js";
|
|
3
3
|
import { Container, SearchBar } from "@firecms/ui";
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
useCollapsedGroups,
|
|
6
|
+
useCustomizationController,
|
|
7
|
+
useFireCMSContext,
|
|
8
|
+
useNavigationController
|
|
9
|
+
} from "../../hooks";
|
|
6
10
|
import {
|
|
7
11
|
CMSAnalyticsEvent,
|
|
8
12
|
NavigationEntry,
|
|
@@ -143,7 +147,6 @@ export function DefaultHomePage({
|
|
|
143
147
|
allProcessed = allProcessed.filter(
|
|
144
148
|
(g) =>
|
|
145
149
|
g.entries.length ||
|
|
146
|
-
groupOrderFromNavController.includes(g.name) ||
|
|
147
150
|
(g.name === DEFAULT_GROUP_NAME && hasPluginAdditionalCards)
|
|
148
151
|
);
|
|
149
152
|
}
|
|
@@ -177,6 +180,7 @@ export function DefaultHomePage({
|
|
|
177
180
|
const persistNavigationGroups = (
|
|
178
181
|
latest: { name: string; entries: NavigationEntry[] }[]
|
|
179
182
|
) => {
|
|
183
|
+
// Map ALL groups including "Views"
|
|
180
184
|
const draggable: NavigationGroupMapping[] = latest.map((g) => ({
|
|
181
185
|
name: g.name,
|
|
182
186
|
entries: g.entries.map((e) => e.path)
|
|
@@ -221,13 +225,14 @@ export function DefaultHomePage({
|
|
|
221
225
|
dialogOpenForGroup,
|
|
222
226
|
setDialogOpenForGroup,
|
|
223
227
|
handleRenameGroup,
|
|
228
|
+
handleDialogClose,
|
|
224
229
|
isHoveringNewGroupDropZone,
|
|
225
230
|
setIsHoveringNewGroupDropZone
|
|
226
231
|
} = useHomePageDnd({
|
|
227
232
|
items,
|
|
228
233
|
setItems: updateItems,
|
|
229
234
|
disabled: !allowDragAndDrop || performingSearch,
|
|
230
|
-
onPersist: persistNavigationGroups,
|
|
235
|
+
onPersist: persistNavigationGroups,
|
|
231
236
|
onGroupMoved: (g) =>
|
|
232
237
|
context.analyticsController?.onAnalyticsEvent?.("home_move_group", {
|
|
233
238
|
name: g
|
|
@@ -355,7 +360,7 @@ export function DefaultHomePage({
|
|
|
355
360
|
items={containers}
|
|
356
361
|
strategy={verticalListSortingStrategy}
|
|
357
362
|
>
|
|
358
|
-
{items.map((groupData) => {
|
|
363
|
+
{items.map((groupData, groupIndex) => {
|
|
359
364
|
const groupKey = groupData.name;
|
|
360
365
|
const entriesInGroup = groupData.entries;
|
|
361
366
|
|
|
@@ -378,14 +383,13 @@ export function DefaultHomePage({
|
|
|
378
383
|
|
|
379
384
|
if (
|
|
380
385
|
entriesInGroup.length === 0 &&
|
|
381
|
-
(AdditionalCards.length === 0 || performingSearch)
|
|
382
|
-
!groupOrderFromNavController.includes(groupKey)
|
|
386
|
+
(AdditionalCards.length === 0 || performingSearch)
|
|
383
387
|
)
|
|
384
388
|
return null;
|
|
385
389
|
|
|
386
390
|
return (
|
|
387
391
|
<SortableNavigationGroup
|
|
388
|
-
key={
|
|
392
|
+
key={`group-${groupIndex}`}
|
|
389
393
|
groupName={groupKey}
|
|
390
394
|
disabled={dndDisabled}
|
|
391
395
|
>
|
|
@@ -557,7 +561,7 @@ export function DefaultHomePage({
|
|
|
557
561
|
existingGroupNames={items
|
|
558
562
|
.map((g) => g.name)
|
|
559
563
|
.filter((n) => n !== dialogOpenForGroup)}
|
|
560
|
-
onClose={
|
|
564
|
+
onClose={handleDialogClose}
|
|
561
565
|
onRename={(newName) => {
|
|
562
566
|
handleRenameGroup(dialogOpenForGroup, newName);
|
|
563
567
|
}}
|