@firecms/core 3.0.0-beta.12 → 3.0.0-beta.14
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/app/Scaffold.d.ts +4 -0
- package/dist/components/ArrayContainer.d.ts +13 -11
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
- package/dist/components/EntityJsonPreview.d.ts +3 -0
- package/dist/components/EntityPreview.d.ts +4 -2
- package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
- package/dist/components/index.d.ts +1 -0
- package/dist/core/DefaultAppBar.d.ts +8 -2
- package/dist/core/EntityEditView.d.ts +8 -1
- package/dist/core/FireCMS.d.ts +2 -2
- package/dist/form/EntityForm.d.ts +5 -1
- package/dist/form/PropertyFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
- package/dist/hooks/data/save.d.ts +1 -1
- package/dist/hooks/useBuildNavigationController.d.ts +2 -9
- package/dist/index.es.js +9251 -8721
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +16686 -16160
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +3 -2
- package/dist/internal/useBuildSideEntityController.d.ts +3 -3
- package/dist/types/collections.d.ts +26 -2
- package/dist/types/fields.d.ts +4 -1
- package/dist/types/firecms.d.ts +3 -2
- package/dist/types/navigation.d.ts +9 -0
- package/dist/types/plugins.d.ts +13 -0
- package/dist/types/properties.d.ts +22 -1
- package/dist/types/side_entity_controller.d.ts +4 -0
- package/dist/util/callbacks.d.ts +2 -0
- package/dist/util/createFormexStub.d.ts +2 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/navigation_utils.d.ts +2 -1
- package/dist/util/objects.d.ts +1 -1
- package/dist/util/property_utils.d.ts +2 -2
- package/dist/util/references.d.ts +2 -2
- package/dist/util/resolutions.d.ts +11 -6
- package/package.json +9 -6
- package/src/app/Scaffold.tsx +13 -3
- package/src/components/ArrayContainer.tsx +414 -282
- package/src/components/ClearFilterSortButton.tsx +1 -1
- package/src/components/ConfirmationDialog.tsx +9 -9
- package/src/components/DeleteEntityDialog.tsx +4 -2
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +8 -6
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +58 -29
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -6
- package/src/components/EntityJsonPreview.tsx +66 -0
- package/src/components/EntityPreview.tsx +27 -16
- package/src/components/EntityView.tsx +4 -1
- package/src/components/ErrorView.tsx +1 -1
- package/src/components/HomePage/DefaultHomePage.tsx +2 -1
- package/src/components/HomePage/NavigationCardBinding.tsx +3 -1
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -1
- package/src/components/SelectableTable/SelectableTable.tsx +140 -143
- package/src/components/VirtualTable/VirtualTable.tsx +37 -31
- package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
- package/src/components/common/default_entity_actions.tsx +15 -4
- package/src/components/index.tsx +2 -0
- package/src/core/DefaultAppBar.tsx +17 -5
- package/src/core/EntityEditView.tsx +135 -47
- package/src/core/EntitySidePanel.tsx +15 -20
- package/src/core/FireCMS.tsx +19 -12
- package/src/form/EntityForm.tsx +32 -24
- package/src/form/PropertyFieldBinding.tsx +8 -6
- package/src/form/components/CustomIdField.tsx +3 -1
- package/src/form/components/LabelWithIcon.tsx +1 -1
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +4 -1
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -2
- package/src/form/field_bindings/BlockFieldBinding.tsx +1 -1
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
- package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +6 -2
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +3 -3
- package/src/form/field_bindings/RepeatFieldBinding.tsx +9 -2
- package/src/form/field_bindings/SelectFieldBinding.tsx +3 -2
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +227 -156
- package/src/hooks/data/save.ts +24 -35
- package/src/hooks/useBuildNavigationController.tsx +42 -17
- package/src/hooks/useFireCMSContext.tsx +0 -30
- package/src/internal/useBuildDataSource.ts +9 -5
- package/src/internal/useBuildSideEntityController.tsx +26 -20
- package/src/preview/PropertyPreview.tsx +4 -2
- package/src/preview/components/ImagePreview.tsx +2 -2
- package/src/preview/components/ReferencePreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +5 -4
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +4 -3
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +4 -2
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -3
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +4 -2
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +4 -2
- package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
- package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
- package/src/routes/FireCMSRoute.tsx +15 -4
- package/src/types/collections.ts +31 -2
- package/src/types/fields.tsx +5 -1
- package/src/types/firecms.tsx +3 -3
- package/src/types/navigation.ts +11 -0
- package/src/types/plugins.tsx +17 -0
- package/src/types/properties.ts +27 -1
- package/src/types/side_entity_controller.tsx +5 -0
- package/src/util/callbacks.ts +119 -0
- package/src/util/createFormexStub.tsx +62 -0
- package/src/util/index.ts +1 -0
- package/src/util/join_collections.ts +3 -1
- package/src/util/navigation_from_path.ts +5 -1
- package/src/util/navigation_utils.ts +84 -20
- package/src/util/objects.ts +54 -17
- package/src/util/property_utils.tsx +7 -3
- package/src/util/references.ts +8 -6
- package/src/util/resolutions.ts +17 -9
- package/src/util/useStorageUploadController.tsx +21 -2
- package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
- package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
|
@@ -2,7 +2,7 @@ import { DeleteIcon, EditIcon, FileCopyIcon } from "@firecms/ui";
|
|
|
2
2
|
import { EntityAction } from "../../types";
|
|
3
3
|
import { DeleteEntityDialog } from "../DeleteEntityDialog";
|
|
4
4
|
import { addRecentId } from "../EntityCollectionView/utils";
|
|
5
|
-
import { navigateToEntity } from "../../util";
|
|
5
|
+
import { navigateToEntity, resolveDefaultSelectedView } from "../../util";
|
|
6
6
|
|
|
7
7
|
export const editEntityAction: EntityAction = {
|
|
8
8
|
icon: <EditIcon/>,
|
|
@@ -30,15 +30,24 @@ export const editEntityAction: EntityAction = {
|
|
|
30
30
|
addRecentId(collection.id, entity.id);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const path = collection?.collectionGroup ?
|
|
33
|
+
const path = collection?.collectionGroup ? collection.id : (fullPath ?? collection?.id ?? entity.path);
|
|
34
|
+
const defaultSelectedView = resolveDefaultSelectedView(
|
|
35
|
+
collection ? collection.defaultSelectedView : undefined,
|
|
36
|
+
{
|
|
37
|
+
status: "existing",
|
|
38
|
+
entityId: entity.id,
|
|
39
|
+
}
|
|
40
|
+
);
|
|
34
41
|
navigateToEntity({
|
|
35
42
|
openEntityMode,
|
|
36
43
|
collection,
|
|
37
44
|
entityId: entity.id,
|
|
38
45
|
path,
|
|
46
|
+
fullIdPath: path,
|
|
39
47
|
sideEntityController: context.sideEntityController,
|
|
40
48
|
onClose: () => unhighlightEntity?.(entity),
|
|
41
|
-
navigation: context.navigation
|
|
49
|
+
navigation: context.navigation,
|
|
50
|
+
selectedTab: defaultSelectedView
|
|
42
51
|
});
|
|
43
52
|
|
|
44
53
|
return Promise.resolve(undefined);
|
|
@@ -63,12 +72,14 @@ export const copyEntityAction: EntityAction = {
|
|
|
63
72
|
path: entity.path,
|
|
64
73
|
entityId: entity.id
|
|
65
74
|
});
|
|
66
|
-
|
|
75
|
+
|
|
76
|
+
const path = collection?.collectionGroup ? collection.id : (fullPath ?? collection?.id ?? entity.path);
|
|
67
77
|
navigateToEntity({
|
|
68
78
|
openEntityMode,
|
|
69
79
|
collection,
|
|
70
80
|
entityId: entity.id,
|
|
71
81
|
path,
|
|
82
|
+
fullIdPath: path,
|
|
72
83
|
copy: true,
|
|
73
84
|
sideEntityController: context.sideEntityController,
|
|
74
85
|
onClose: () => unhighlightEntity?.(entity),
|
package/src/components/index.tsx
CHANGED
|
@@ -22,12 +22,19 @@ import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
|
|
|
22
22
|
|
|
23
23
|
export type DefaultAppBarProps<ADDITIONAL_PROPS = object> = {
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* The content of the app bar, usually a title or logo. This includes a link to the home page.
|
|
27
|
+
*/
|
|
25
28
|
title?: React.ReactNode;
|
|
29
|
+
|
|
26
30
|
/**
|
|
27
|
-
* A component that gets rendered on the upper side of the main toolbar
|
|
31
|
+
* A component that gets rendered on the upper side to the end of the main toolbar
|
|
28
32
|
*/
|
|
29
33
|
endAdornment?: React.ReactNode;
|
|
30
34
|
|
|
35
|
+
/**
|
|
36
|
+
* A component that gets rendered on the upper side to the start of the main toolbar
|
|
37
|
+
*/
|
|
31
38
|
startAdornment?: React.ReactNode;
|
|
32
39
|
|
|
33
40
|
dropDownActions?: React.ReactNode;
|
|
@@ -57,14 +64,18 @@ export const DefaultAppBar = function DefaultAppBar({
|
|
|
57
64
|
includeModeToggle = true,
|
|
58
65
|
className,
|
|
59
66
|
style,
|
|
60
|
-
user: userProp
|
|
67
|
+
user: userProp,
|
|
68
|
+
logo: logoProp,
|
|
61
69
|
}: DefaultAppBarProps) {
|
|
62
70
|
|
|
63
71
|
const {
|
|
64
72
|
hasDrawer,
|
|
65
73
|
drawerOpen,
|
|
66
|
-
logo
|
|
74
|
+
logo: appLogo
|
|
67
75
|
} = useApp();
|
|
76
|
+
|
|
77
|
+
const logo = logoProp ?? appLogo;
|
|
78
|
+
|
|
68
79
|
const navigation = useNavigationController();
|
|
69
80
|
|
|
70
81
|
const breadcrumbs = useBreadcrumbsController();
|
|
@@ -134,7 +145,7 @@ export const DefaultAppBar = function DefaultAppBar({
|
|
|
134
145
|
</Link>
|
|
135
146
|
</div>}
|
|
136
147
|
|
|
137
|
-
{breadcrumbs.breadcrumbs && <div className="mr-8 hidden lg:block">
|
|
148
|
+
{(breadcrumbs.breadcrumbs ?? []).length > 0 && <div className="mr-8 hidden lg:block">
|
|
138
149
|
<div className={"flex flex-row gap-2"}>
|
|
139
150
|
{breadcrumbs.breadcrumbs.map((breadcrumb, index) => {
|
|
140
151
|
return <React.Fragment key={breadcrumb.url + "_" + index}>
|
|
@@ -176,7 +187,8 @@ export const DefaultAppBar = function DefaultAppBar({
|
|
|
176
187
|
</IconButton>}>
|
|
177
188
|
<MenuItem onClick={() => setMode("dark")}><DarkModeIcon size={"smallest"}/> Dark</MenuItem>
|
|
178
189
|
<MenuItem onClick={() => setMode("light")}><LightModeIcon size={"smallest"}/> Light </MenuItem>
|
|
179
|
-
<MenuItem onClick={() => setMode("system")}> <BrightnessMediumIcon
|
|
190
|
+
<MenuItem onClick={() => setMode("system")}> <BrightnessMediumIcon
|
|
191
|
+
size={"smallest"}/>System</MenuItem>
|
|
180
192
|
</Menu>}
|
|
181
193
|
|
|
182
194
|
<Menu trigger={avatarComponent}>
|
|
@@ -5,6 +5,7 @@ import { CircularProgressCenter, EntityCollectionView, EntityView, ErrorBoundary
|
|
|
5
5
|
import {
|
|
6
6
|
canEditEntity,
|
|
7
7
|
removeInitialAndTrailingSlashes,
|
|
8
|
+
resolveCollection,
|
|
8
9
|
resolveDefaultSelectedView,
|
|
9
10
|
resolvedSelectedEntityView
|
|
10
11
|
} from "../util";
|
|
@@ -16,12 +17,15 @@ import {
|
|
|
16
17
|
useFireCMSContext,
|
|
17
18
|
useLargeLayout
|
|
18
19
|
} from "../hooks";
|
|
19
|
-
import { CircularProgress, cls, defaultBorderMixin, Tab, Tabs, Typography } from "@firecms/ui";
|
|
20
|
+
import { CircularProgress, cls, CodeIcon, defaultBorderMixin, Tab, Tabs, Typography } from "@firecms/ui";
|
|
20
21
|
import { getEntityFromCache } from "../util/entity_cache";
|
|
21
22
|
import { EntityForm, EntityFormProps } from "../form";
|
|
22
23
|
import { EntityEditViewFormActions } from "./EntityEditViewFormActions";
|
|
24
|
+
import { EntityJsonPreview } from "../components/EntityJsonPreview";
|
|
25
|
+
import { createFormexStub } from "../util/createFormexStub";
|
|
23
26
|
|
|
24
|
-
const MAIN_TAB_VALUE = "
|
|
27
|
+
export const MAIN_TAB_VALUE = "__main_##Q$SC^#S6";
|
|
28
|
+
export const JSON_TAB_VALUE = "__json";
|
|
25
29
|
|
|
26
30
|
export type OnUpdateParams = {
|
|
27
31
|
entity: Entity<any>,
|
|
@@ -41,6 +45,10 @@ export type OnTabChangeParams<M extends Record<string, any>> = {
|
|
|
41
45
|
|
|
42
46
|
export interface EntityEditViewProps<M extends Record<string, any>> {
|
|
43
47
|
path: string;
|
|
48
|
+
/**
|
|
49
|
+
* The navigation path to the entity.
|
|
50
|
+
*/
|
|
51
|
+
fullIdPath?: string;
|
|
44
52
|
collection: EntityCollection<M>;
|
|
45
53
|
entityId?: string;
|
|
46
54
|
databaseId?: string;
|
|
@@ -52,7 +60,7 @@ export interface EntityEditViewProps<M extends Record<string, any>> {
|
|
|
52
60
|
onTabChange?: (props: OnTabChangeParams<M>) => void;
|
|
53
61
|
layout?: "side_panel" | "full_screen";
|
|
54
62
|
barActions?: React.ReactNode;
|
|
55
|
-
formProps?: Partial<EntityFormProps<M
|
|
63
|
+
formProps?: Partial<EntityFormProps<M>>,
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
/**
|
|
@@ -102,19 +110,6 @@ export function EntityEditView<M extends Record<string, any>, USER extends User>
|
|
|
102
110
|
console.error(`Entity with id ${entityId} not found in collection ${props.path}`);
|
|
103
111
|
}
|
|
104
112
|
|
|
105
|
-
if (!canEdit) {
|
|
106
|
-
return <div className={"flex flex-col"}>
|
|
107
|
-
<Typography className={"mt-16 mb-8 mx-8"} variant={"h4"}>
|
|
108
|
-
{props.collection.singularName ?? props.collection.name}
|
|
109
|
-
</Typography>
|
|
110
|
-
<EntityView
|
|
111
|
-
className={"px-8"}
|
|
112
|
-
entity={entity as Entity<M>}
|
|
113
|
-
path={props.path}
|
|
114
|
-
collection={props.collection}/>
|
|
115
|
-
</div>
|
|
116
|
-
}
|
|
117
|
-
|
|
118
113
|
return <EntityEditViewInner<M> {...props}
|
|
119
114
|
entityId={entityId}
|
|
120
115
|
entity={entity}
|
|
@@ -122,11 +117,13 @@ export function EntityEditView<M extends Record<string, any>, USER extends User>
|
|
|
122
117
|
dataLoading={dataLoading}
|
|
123
118
|
status={status}
|
|
124
119
|
setStatus={setStatus}
|
|
120
|
+
canEdit={canEdit}
|
|
125
121
|
/>;
|
|
126
122
|
}
|
|
127
123
|
|
|
128
124
|
export function EntityEditViewInner<M extends Record<string, any>>({
|
|
129
125
|
path,
|
|
126
|
+
fullIdPath,
|
|
130
127
|
entityId,
|
|
131
128
|
selectedTab: selectedTabProp,
|
|
132
129
|
collection,
|
|
@@ -142,12 +139,14 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
142
139
|
status,
|
|
143
140
|
setStatus,
|
|
144
141
|
formProps,
|
|
142
|
+
canEdit
|
|
145
143
|
}: EntityEditViewProps<M> & {
|
|
146
144
|
entity?: Entity<M>,
|
|
147
145
|
cachedDirtyValues?: Partial<M>, // dirty cached entity in memory
|
|
148
146
|
dataLoading: boolean,
|
|
149
147
|
status: EntityStatus,
|
|
150
148
|
setStatus: (status: EntityStatus) => void,
|
|
149
|
+
canEdit?: boolean,
|
|
151
150
|
}) {
|
|
152
151
|
|
|
153
152
|
const context = useFireCMSContext();
|
|
@@ -175,40 +174,77 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
175
174
|
), []);
|
|
176
175
|
|
|
177
176
|
const [selectedTab, setSelectedTab] = useState<string>(selectedTabProp ?? defaultSelectedView ?? MAIN_TAB_VALUE);
|
|
178
|
-
|
|
179
177
|
useEffect(() => {
|
|
180
|
-
if ((selectedTabProp ?? MAIN_TAB_VALUE) !== selectedTab) {
|
|
181
|
-
setSelectedTab(selectedTabProp ?? MAIN_TAB_VALUE);
|
|
178
|
+
if ((selectedTabProp ?? defaultSelectedView ?? MAIN_TAB_VALUE) !== selectedTab) {
|
|
179
|
+
setSelectedTab(selectedTabProp ?? defaultSelectedView ?? MAIN_TAB_VALUE);
|
|
182
180
|
}
|
|
183
181
|
}, [selectedTabProp]);
|
|
184
182
|
|
|
185
183
|
const subcollections = (collection.subcollections ?? []).filter(c => !c.hideFromNavigation);
|
|
186
184
|
const subcollectionsCount = subcollections?.length ?? 0;
|
|
187
|
-
const customViews = collection.entityViews;
|
|
185
|
+
const customViews = collection.entityViews ?? [];
|
|
188
186
|
const customViewsCount = customViews?.length ?? 0;
|
|
189
|
-
const
|
|
187
|
+
const includeJsonView = collection.includeJsonView === undefined ? true : collection.includeJsonView;
|
|
188
|
+
const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0 || includeJsonView;
|
|
189
|
+
|
|
190
|
+
const plugins = customizationController.plugins;
|
|
190
191
|
|
|
191
192
|
const {
|
|
192
193
|
resolvedEntityViews,
|
|
193
194
|
selectedEntityView,
|
|
194
195
|
selectedSecondaryForm
|
|
195
|
-
} = resolvedSelectedEntityView(customViews, customizationController, selectedTab);
|
|
196
|
+
} = resolvedSelectedEntityView(customViews, customizationController, selectedTab, canEdit);
|
|
196
197
|
|
|
197
198
|
const actionsAtTheBottom = !largeLayout || layout === "side_panel" || selectedEntityView?.includeActions === "bottom";
|
|
198
199
|
|
|
199
200
|
const mainViewVisible = selectedTab === MAIN_TAB_VALUE || Boolean(selectedSecondaryForm);
|
|
200
201
|
|
|
202
|
+
const authController = useAuthController();
|
|
203
|
+
|
|
201
204
|
const customViewsView: React.ReactNode[] | undefined = customViews && resolvedEntityViews
|
|
202
205
|
.filter(e => !e.includeActions)
|
|
203
206
|
.map((customView) => {
|
|
207
|
+
|
|
204
208
|
if (!customView)
|
|
205
209
|
return null;
|
|
206
210
|
const Builder = customView.Builder;
|
|
207
211
|
if (!Builder) {
|
|
208
|
-
console.error("customView.Builder is not defined");
|
|
212
|
+
console.error("INTERNAL: customView.Builder is not defined");
|
|
209
213
|
return null;
|
|
210
214
|
}
|
|
211
215
|
|
|
216
|
+
if (!entityId) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const formexStub = createFormexStub<M>(usedEntity?.values ?? {} as M);
|
|
221
|
+
const usedFormContext: FormContext = formContext ?? {
|
|
222
|
+
entityId,
|
|
223
|
+
disabled: false,
|
|
224
|
+
openEntityMode: layout,
|
|
225
|
+
status: status,
|
|
226
|
+
values: usedEntity?.values ?? {},
|
|
227
|
+
setFieldValue: (key: string, value: any) => {
|
|
228
|
+
throw new Error("You can't update values in read only mode");
|
|
229
|
+
},
|
|
230
|
+
save: () => {
|
|
231
|
+
throw new Error("You can't save in read only mode");
|
|
232
|
+
},
|
|
233
|
+
collection: resolveCollection<M>({
|
|
234
|
+
collection,
|
|
235
|
+
path,
|
|
236
|
+
entityId,
|
|
237
|
+
values: usedEntity?.values ?? {},
|
|
238
|
+
previousValues: usedEntity?.values ?? {},
|
|
239
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
240
|
+
authController
|
|
241
|
+
}),
|
|
242
|
+
path,
|
|
243
|
+
entity: usedEntity,
|
|
244
|
+
savingError: undefined,
|
|
245
|
+
formex: formexStub
|
|
246
|
+
};
|
|
247
|
+
|
|
212
248
|
return <div
|
|
213
249
|
className={cls(defaultBorderMixin,
|
|
214
250
|
"relative flex-1 w-full h-full overflow-auto",
|
|
@@ -217,11 +253,12 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
217
253
|
key={`custom_view_${customView.key}`}
|
|
218
254
|
role="tabpanel">
|
|
219
255
|
<ErrorBoundary>
|
|
220
|
-
{
|
|
256
|
+
{usedFormContext && <Builder
|
|
221
257
|
collection={collection}
|
|
258
|
+
parentCollectionIds={parentCollectionIds}
|
|
222
259
|
entity={usedEntity}
|
|
223
|
-
modifiedValues={
|
|
224
|
-
formContext={
|
|
260
|
+
modifiedValues={usedFormContext?.formex?.values ?? usedEntity?.values}
|
|
261
|
+
formContext={usedFormContext}
|
|
225
262
|
/>}
|
|
226
263
|
</ErrorBoundary>
|
|
227
264
|
</div>;
|
|
@@ -229,9 +266,22 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
229
266
|
|
|
230
267
|
const globalLoading = dataLoading && !usedEntity;
|
|
231
268
|
|
|
269
|
+
const jsonView = <div
|
|
270
|
+
className={cls("relative flex-1 h-full overflow-auto w-full",
|
|
271
|
+
{ "hidden": selectedTab !== JSON_TAB_VALUE })}
|
|
272
|
+
key={"json_view"}
|
|
273
|
+
role="tabpanel">
|
|
274
|
+
<ErrorBoundary>
|
|
275
|
+
<EntityJsonPreview
|
|
276
|
+
values={formContext?.values ?? entity?.values ?? {}}/>
|
|
277
|
+
</ErrorBoundary>
|
|
278
|
+
</div>;
|
|
279
|
+
|
|
232
280
|
const subCollectionsViews = subcollections && subcollections.map((subcollection) => {
|
|
233
281
|
const subcollectionId = subcollection.id ?? subcollection.path;
|
|
234
|
-
const
|
|
282
|
+
const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollectionId)}` : undefined;
|
|
283
|
+
const newFullIdPath = fullIdPath ? `${fullIdPath}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollectionId)}` : undefined;
|
|
284
|
+
|
|
235
285
|
if (selectedTab !== subcollectionId) return null;
|
|
236
286
|
return (
|
|
237
287
|
<div
|
|
@@ -242,9 +292,10 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
242
292
|
{globalLoading && <CircularProgressCenter/>}
|
|
243
293
|
|
|
244
294
|
{!globalLoading &&
|
|
245
|
-
(usedEntity &&
|
|
295
|
+
(usedEntity && newFullPath
|
|
246
296
|
? <EntityCollectionView
|
|
247
|
-
fullPath={
|
|
297
|
+
fullPath={newFullPath}
|
|
298
|
+
fullIdPath={newFullIdPath}
|
|
248
299
|
parentCollectionIds={[...parentCollectionIds, collection.id]}
|
|
249
300
|
isSubCollection={true}
|
|
250
301
|
updateUrl={false}
|
|
@@ -266,15 +317,30 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
266
317
|
setSelectedTab(value);
|
|
267
318
|
if (status === "existing") {
|
|
268
319
|
onTabChange?.({
|
|
269
|
-
path,
|
|
320
|
+
path: fullIdPath ?? path,
|
|
270
321
|
entityId,
|
|
271
322
|
selectedTab: value === MAIN_TAB_VALUE ? undefined : value,
|
|
272
|
-
collection
|
|
323
|
+
collection
|
|
273
324
|
});
|
|
274
325
|
}
|
|
275
326
|
};
|
|
276
327
|
|
|
277
|
-
|
|
328
|
+
const entityReadOnlyView = !canEdit && entity ? <div
|
|
329
|
+
className={cls("flex-1 flex flex-row w-full overflow-y-auto justify-center", (canEdit || !mainViewVisible || selectedSecondaryForm) ? "hidden" : "")}>
|
|
330
|
+
<div
|
|
331
|
+
className={cls("relative flex flex-col max-w-4xl lg:max-w-3xl xl:max-w-4xl 2xl:max-w-6xl w-full h-fit")}>
|
|
332
|
+
<Typography className={"mt-16 mb-8 mx-8"} variant={"h4"}>
|
|
333
|
+
{collection.singularName ?? collection.name}
|
|
334
|
+
</Typography>
|
|
335
|
+
<EntityView
|
|
336
|
+
className={"px-8 h-full overflow-auto"}
|
|
337
|
+
entity={entity}
|
|
338
|
+
path={path}
|
|
339
|
+
collection={collection}/>
|
|
340
|
+
<div className="h-16"/>
|
|
341
|
+
</div>
|
|
342
|
+
</div> : null;
|
|
343
|
+
|
|
278
344
|
const entityView = <EntityForm<M>
|
|
279
345
|
collection={collection}
|
|
280
346
|
path={path}
|
|
@@ -285,8 +351,9 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
285
351
|
openEntityMode={layout}
|
|
286
352
|
forceActionsAtTheBottom={actionsAtTheBottom}
|
|
287
353
|
initialStatus={status}
|
|
288
|
-
className={cls(!mainViewVisible ? "hidden" : "", formProps?.className)}
|
|
354
|
+
className={cls((!mainViewVisible || !canEdit) && !selectedSecondaryForm ? "hidden" : "", formProps?.className)}
|
|
289
355
|
EntityFormActionsComponent={EntityEditViewFormActions}
|
|
356
|
+
disabled={!canEdit}
|
|
290
357
|
{...formProps}
|
|
291
358
|
onEntityChange={(entity) => {
|
|
292
359
|
setUsedEntity(entity);
|
|
@@ -303,7 +370,7 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
303
370
|
onSaved={(params) => {
|
|
304
371
|
const res = {
|
|
305
372
|
...params,
|
|
306
|
-
selectedTab: MAIN_TAB_VALUE === selectedTab ? undefined : selectedTab
|
|
373
|
+
selectedTab: MAIN_TAB_VALUE === selectedTab ? undefined : selectedTab
|
|
307
374
|
};
|
|
308
375
|
onSaved?.(res);
|
|
309
376
|
formProps?.onSaved?.(res);
|
|
@@ -320,14 +387,24 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
320
387
|
</Tab>
|
|
321
388
|
);
|
|
322
389
|
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
390
|
+
const customViewTabsStart = resolvedEntityViews.filter(view => view.position === "start")
|
|
391
|
+
.map((view) =>
|
|
392
|
+
<Tab
|
|
393
|
+
className={!view.tabComponent ? "text-sm min-w-[120px]" : undefined}
|
|
394
|
+
value={view.key}
|
|
395
|
+
key={`entity_detail_collection_tab_${view.name}`}>
|
|
396
|
+
{view.tabComponent ?? view.name}
|
|
397
|
+
</Tab>
|
|
398
|
+
);
|
|
399
|
+
const customViewTabsEnd = resolvedEntityViews.filter(view => !view.position || view.position === "end")
|
|
400
|
+
.map((view) =>
|
|
401
|
+
<Tab
|
|
402
|
+
className={!view.tabComponent ? "text-sm min-w-[120px]" : undefined}
|
|
403
|
+
value={view.key}
|
|
404
|
+
key={`entity_detail_collection_tab_${view.name}`}>
|
|
405
|
+
{view.tabComponent ?? view.name}
|
|
406
|
+
</Tab>
|
|
407
|
+
);
|
|
331
408
|
|
|
332
409
|
const shouldShowTopBar = Boolean(barActions) || hasAdditionalViews;
|
|
333
410
|
|
|
@@ -350,6 +427,15 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
350
427
|
onSideTabClick(value);
|
|
351
428
|
}}>
|
|
352
429
|
|
|
430
|
+
{includeJsonView && <Tab
|
|
431
|
+
disabled={!hasAdditionalViews}
|
|
432
|
+
value={JSON_TAB_VALUE}
|
|
433
|
+
className={"text-sm"}>
|
|
434
|
+
<CodeIcon size={"small"}/>
|
|
435
|
+
</Tab>}
|
|
436
|
+
|
|
437
|
+
{customViewTabsStart}
|
|
438
|
+
|
|
353
439
|
<Tab
|
|
354
440
|
disabled={!hasAdditionalViews}
|
|
355
441
|
value={MAIN_TAB_VALUE}
|
|
@@ -357,7 +443,8 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
357
443
|
{collection.singularName ?? collection.name}
|
|
358
444
|
</Tab>
|
|
359
445
|
|
|
360
|
-
|
|
446
|
+
|
|
447
|
+
{customViewTabsEnd}
|
|
361
448
|
|
|
362
449
|
{subcollectionTabs}
|
|
363
450
|
</Tabs>}
|
|
@@ -367,9 +454,12 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
367
454
|
? <div className="w-full pt-12 pb-16 px-4 sm:px-8 md:px-10">
|
|
368
455
|
<CircularProgressCenter/>
|
|
369
456
|
</div>
|
|
370
|
-
:
|
|
457
|
+
: <>
|
|
458
|
+
{entityReadOnlyView}
|
|
459
|
+
{entityView}
|
|
460
|
+
</>}
|
|
371
461
|
|
|
372
|
-
{
|
|
462
|
+
{jsonView}
|
|
373
463
|
|
|
374
464
|
{customViewsView}
|
|
375
465
|
|
|
@@ -377,8 +467,6 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
377
467
|
|
|
378
468
|
</div>;
|
|
379
469
|
|
|
380
|
-
const plugins = customizationController.plugins;
|
|
381
|
-
|
|
382
470
|
if (plugins) {
|
|
383
471
|
plugins.forEach((plugin: FireCMSPlugin) => {
|
|
384
472
|
if (plugin.form?.provider) {
|
|
@@ -19,11 +19,18 @@ import { useLocation, useNavigate } from "react-router-dom";
|
|
|
19
19
|
*/
|
|
20
20
|
export function EntitySidePanel(props: EntitySidePanelProps) {
|
|
21
21
|
|
|
22
|
+
const {
|
|
23
|
+
allowFullScreen = true,
|
|
24
|
+
path,
|
|
25
|
+
entityId,
|
|
26
|
+
formProps,
|
|
27
|
+
} = props;
|
|
28
|
+
|
|
22
29
|
const {
|
|
23
30
|
blocked,
|
|
24
31
|
setBlocked,
|
|
25
32
|
setBlockedNavigationMessage,
|
|
26
|
-
close
|
|
33
|
+
close,
|
|
27
34
|
} = useSideDialogContext();
|
|
28
35
|
|
|
29
36
|
const navigate = useNavigate();
|
|
@@ -64,22 +71,10 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
|
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
const parentCollectionIds = useMemo(() => {
|
|
67
|
-
return navigationController.getParentCollectionIds(
|
|
68
|
-
}, [navigationController,
|
|
69
|
-
|
|
70
|
-
const collection = useMemo(() => {
|
|
71
|
-
if (props.collection) {
|
|
72
|
-
return props.collection;
|
|
73
|
-
}
|
|
74
|
+
return navigationController.getParentCollectionIds(path);
|
|
75
|
+
}, [navigationController, path]);
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
if (registryCollection) {
|
|
77
|
-
return registryCollection;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
console.error("ERROR: No collection found in path `", props.path, "`. Entity id: ", props.entityId);
|
|
81
|
-
throw Error("ERROR: No collection found in path `" + props.path + "`. Make sure you have defined a collection for this path in the root navigation.");
|
|
82
|
-
}, [navigationController, props.collection]);
|
|
77
|
+
const collection = props.collection ?? navigationController.getCollection(path);
|
|
83
78
|
|
|
84
79
|
useEffect(() => {
|
|
85
80
|
function beforeunload(e: any) {
|
|
@@ -126,16 +121,16 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
|
|
|
126
121
|
onClick={onClose}>
|
|
127
122
|
<CloseIcon size={"small"}/>
|
|
128
123
|
</IconButton>
|
|
129
|
-
<IconButton
|
|
124
|
+
{allowFullScreen && <IconButton
|
|
130
125
|
className="self-center"
|
|
131
126
|
onClick={() => {
|
|
132
|
-
if (
|
|
127
|
+
if (entityId)
|
|
133
128
|
navigate(location.pathname);
|
|
134
129
|
else
|
|
135
130
|
navigate(location.pathname + "#new");
|
|
136
131
|
}}>
|
|
137
132
|
<OpenInFullIcon size={"small"}/>
|
|
138
|
-
</IconButton>
|
|
133
|
+
</IconButton>}
|
|
139
134
|
</>}
|
|
140
135
|
onTabChange={({
|
|
141
136
|
path,
|
|
@@ -151,7 +146,7 @@ export function EntitySidePanel(props: EntitySidePanelProps) {
|
|
|
151
146
|
collection,
|
|
152
147
|
});
|
|
153
148
|
}}
|
|
154
|
-
formProps={
|
|
149
|
+
formProps={formProps}
|
|
155
150
|
/>
|
|
156
151
|
</ErrorBoundary>
|
|
157
152
|
|
package/src/core/FireCMS.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { useMemo } from "react";
|
|
4
4
|
import { CenteredView, Typography } from "@firecms/ui";
|
|
5
|
-
import { CustomizationController,
|
|
5
|
+
import { CustomizationController, FireCMSContext, FireCMSPlugin, FireCMSProps, User } from "../types";
|
|
6
6
|
import { AuthControllerContext } from "../contexts";
|
|
7
7
|
import { useBuildSideEntityController } from "../internal/useBuildSideEntityController";
|
|
8
8
|
import { useCustomizationController, useFireCMSContext } from "../hooks";
|
|
@@ -33,7 +33,7 @@ import { BreadcrumbsProvider } from "../contexts/BreacrumbsContext";
|
|
|
33
33
|
|
|
34
34
|
* @group Core
|
|
35
35
|
*/
|
|
36
|
-
export function FireCMS<USER extends User
|
|
36
|
+
export function FireCMS<USER extends User>(props: FireCMSProps<USER>) {
|
|
37
37
|
|
|
38
38
|
const {
|
|
39
39
|
children,
|
|
@@ -44,7 +44,7 @@ export function FireCMS<USER extends User, EC extends EntityCollection>(props: F
|
|
|
44
44
|
authController,
|
|
45
45
|
storageSource,
|
|
46
46
|
dataSourceDelegate,
|
|
47
|
-
plugins,
|
|
47
|
+
plugins: pluginsProp,
|
|
48
48
|
onAnalyticsEvent,
|
|
49
49
|
propertyConfigs,
|
|
50
50
|
entityViews,
|
|
@@ -53,17 +53,14 @@ export function FireCMS<USER extends User, EC extends EntityCollection>(props: F
|
|
|
53
53
|
apiKey
|
|
54
54
|
} = props;
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
propertyConfigs,
|
|
62
|
-
navigationController
|
|
63
|
-
});
|
|
56
|
+
if (pluginsProp) {
|
|
57
|
+
console.warn("The `plugins` prop is deprecated in the FireCMS component. You should pass your plugins to `useBuildNavigationController` instead.");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const plugins = navigationController.plugins ?? pluginsProp;
|
|
64
61
|
|
|
65
62
|
const sideDialogsController = useBuildSideDialogsController();
|
|
66
|
-
const sideEntityController = useBuildSideEntityController(navigationController, sideDialogsController);
|
|
63
|
+
const sideEntityController = useBuildSideEntityController(navigationController, sideDialogsController, authController);
|
|
67
64
|
|
|
68
65
|
const pluginsLoading = plugins?.some(p => p.loading) ?? false;
|
|
69
66
|
|
|
@@ -90,6 +87,16 @@ export function FireCMS<USER extends User, EC extends EntityCollection>(props: F
|
|
|
90
87
|
plugins
|
|
91
88
|
});
|
|
92
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Controller in charge of fetching and persisting data
|
|
92
|
+
*/
|
|
93
|
+
const dataSource = useBuildDataSource({
|
|
94
|
+
delegate: dataSourceDelegate,
|
|
95
|
+
propertyConfigs,
|
|
96
|
+
navigationController,
|
|
97
|
+
authController
|
|
98
|
+
});
|
|
99
|
+
|
|
93
100
|
if (accessResponse?.message) {
|
|
94
101
|
console.warn(accessResponse.message);
|
|
95
102
|
}
|