@firecms/core 3.0.0-beta.13 → 3.0.0-beta.15
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/ArrayContainer.d.ts +7 -12
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
- package/dist/components/EntityPreview.d.ts +4 -2
- package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
- package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
- package/dist/components/HomePage/NavigationCard.d.ts +3 -1
- package/dist/components/HomePage/NavigationCardBinding.d.ts +3 -2
- package/dist/components/HomePage/NavigationGroup.d.ts +7 -1
- package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
- 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/EntityEditView.d.ts +3 -0
- package/dist/core/EntityEditViewFormActions.d.ts +1 -1
- package/dist/core/field_configs.d.ts +1 -1
- package/dist/form/EntityForm.d.ts +2 -1
- package/dist/form/EntityFormActions.d.ts +6 -2
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
- package/dist/form/index.d.ts +1 -0
- package/dist/hooks/useBuildNavigationController.d.ts +50 -8
- package/dist/index.es.js +3259 -1649
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +3265 -1658
- package/dist/index.umd.js.map +1 -1
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/collections.d.ts +28 -4
- package/dist/types/customization_controller.d.ts +8 -0
- package/dist/types/entity_actions.d.ts +46 -6
- package/dist/types/firecms.d.ts +10 -1
- package/dist/types/navigation.d.ts +26 -5
- package/dist/types/plugins.d.ts +32 -1
- package/dist/types/properties.d.ts +7 -0
- package/dist/types/property_config.d.ts +1 -1
- package/dist/types/side_entity_controller.d.ts +8 -0
- package/dist/util/callbacks.d.ts +2 -0
- package/dist/util/icons.d.ts +2 -2
- package/dist/util/index.d.ts +1 -0
- package/dist/util/navigation_from_path.d.ts +4 -0
- package/dist/util/resolutions.d.ts +2 -1
- package/package.json +8 -6
- package/src/components/ArrayContainer.tsx +409 -294
- package/src/components/ClearFilterSortButton.tsx +1 -1
- package/src/components/ConfirmationDialog.tsx +10 -9
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +6 -0
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +26 -4
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +53 -27
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +11 -4
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +3 -2
- package/src/components/EntityPreview.tsx +18 -14
- package/src/components/ErrorView.tsx +1 -1
- package/src/components/FireCMSLogo.tsx +7 -51
- package/src/components/HomePage/DefaultHomePage.tsx +491 -156
- package/src/components/HomePage/FavouritesView.tsx +3 -3
- package/src/components/HomePage/HomePageDnD.tsx +599 -0
- package/src/components/HomePage/NavigationCard.tsx +47 -38
- package/src/components/HomePage/NavigationCardBinding.tsx +13 -7
- package/src/components/HomePage/NavigationGroup.tsx +63 -29
- package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
- package/src/components/SelectableTable/SelectableTable.tsx +140 -143
- package/src/components/UnsavedChangesDialog.tsx +6 -2
- package/src/components/VirtualTable/VirtualTable.tsx +8 -30
- 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 +25 -9
- package/src/components/common/useDataSourceTableController.tsx +2 -2
- package/src/components/index.tsx +2 -0
- package/src/core/DefaultDrawer.tsx +8 -8
- package/src/core/DrawerNavigationItem.tsx +1 -1
- package/src/core/EntityEditView.tsx +65 -18
- package/src/core/EntityEditViewFormActions.tsx +154 -29
- package/src/core/EntitySidePanel.tsx +19 -21
- package/src/core/FireCMS.tsx +9 -1
- package/src/core/field_configs.tsx +15 -1
- package/src/form/EntityForm.tsx +37 -5
- package/src/form/EntityFormActions.tsx +51 -9
- package/src/form/PropertyFieldBinding.tsx +0 -1
- package/src/form/components/CustomIdField.tsx +3 -1
- package/src/form/components/LabelWithIcon.tsx +1 -1
- package/src/form/components/StorageItemPreview.tsx +1 -1
- package/src/form/components/StorageUploadProgress.tsx +3 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -2
- package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -2
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
- package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
- package/src/form/field_bindings/SelectFieldBinding.tsx +2 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +216 -153
- package/src/form/index.tsx +1 -0
- package/src/hooks/data/save.ts +0 -6
- package/src/hooks/useBuildNavigationController.tsx +299 -96
- package/src/internal/useBuildSideEntityController.tsx +8 -5
- package/src/preview/PropertyPreview.tsx +14 -0
- package/src/preview/components/ImagePreview.tsx +2 -2
- package/src/preview/components/ReferencePreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
- 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 +3 -3
- package/src/types/analytics.ts +3 -0
- package/src/types/collections.ts +32 -4
- package/src/types/customization_controller.tsx +9 -0
- package/src/types/entity_actions.tsx +57 -6
- package/src/types/firecms.tsx +11 -2
- package/src/types/navigation.ts +34 -6
- package/src/types/plugins.tsx +40 -1
- package/src/types/properties.ts +8 -0
- package/src/types/property_config.tsx +1 -0
- package/src/types/side_entity_controller.tsx +10 -5
- package/src/util/callbacks.ts +119 -0
- package/src/util/icons.tsx +22 -7
- package/src/util/index.ts +1 -0
- package/src/util/join_collections.ts +3 -1
- package/src/util/navigation_from_path.ts +15 -5
- package/src/util/navigation_utils.ts +72 -57
- package/src/util/objects.ts +53 -20
- package/src/util/resolutions.ts +13 -1
- package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
- package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
package/src/form/EntityForm.tsx
CHANGED
|
@@ -32,7 +32,8 @@ import {
|
|
|
32
32
|
useAuthController,
|
|
33
33
|
useCustomizationController,
|
|
34
34
|
useDataSource,
|
|
35
|
-
useFireCMSContext,
|
|
35
|
+
useFireCMSContext, useNavigationController,
|
|
36
|
+
useSideEntityController,
|
|
36
37
|
useSnackbarController
|
|
37
38
|
} from "../hooks";
|
|
38
39
|
import { Alert, CheckIcon, Chip, cls, EditIcon, NotesIcon, paperMixin, Tooltip, Typography } from "@firecms/ui";
|
|
@@ -57,6 +58,7 @@ export type OnUpdateParams = {
|
|
|
57
58
|
|
|
58
59
|
export type EntityFormProps<M extends Record<string, any>> = {
|
|
59
60
|
path: string;
|
|
61
|
+
fullIdPath?: string;
|
|
60
62
|
collection: EntityCollection<M>;
|
|
61
63
|
entityId?: string;
|
|
62
64
|
entity?: Entity<M>;
|
|
@@ -96,6 +98,7 @@ export type EntityFormProps<M extends Record<string, any>> = {
|
|
|
96
98
|
|
|
97
99
|
export function EntityForm<M extends Record<string, any>>({
|
|
98
100
|
path,
|
|
101
|
+
fullIdPath,
|
|
99
102
|
entityId: entityIdProp,
|
|
100
103
|
collection,
|
|
101
104
|
onValuesModified,
|
|
@@ -123,6 +126,19 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
123
126
|
console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
|
|
124
127
|
}
|
|
125
128
|
|
|
129
|
+
|
|
130
|
+
const sideEntityController = useSideEntityController();
|
|
131
|
+
const navigationController = useNavigationController();
|
|
132
|
+
|
|
133
|
+
const navigateBack = useCallback(() => {
|
|
134
|
+
if (openEntityMode === "side_panel") {
|
|
135
|
+
// If we are in side panel mode, we close the side panel
|
|
136
|
+
sideEntityController.close();
|
|
137
|
+
} else {
|
|
138
|
+
window.history.back();
|
|
139
|
+
}
|
|
140
|
+
}, []);
|
|
141
|
+
|
|
126
142
|
const authController = useAuthController();
|
|
127
143
|
const [status, setStatus] = useState<EntityStatus>(initialStatus);
|
|
128
144
|
|
|
@@ -433,14 +449,16 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
433
449
|
const plugins = customizationController.plugins;
|
|
434
450
|
|
|
435
451
|
const actionsDisabled = disabled || formex.isSubmitting || (status === "existing" && !formex.dirty) || Boolean(disabledProp);
|
|
452
|
+
const parentCollectionIds = navigationController.getParentCollectionIds(path);
|
|
453
|
+
|
|
436
454
|
if (plugins && collection) {
|
|
437
455
|
const actionProps: PluginFormActionProps = {
|
|
438
456
|
entityId,
|
|
457
|
+
parentCollectionIds,
|
|
439
458
|
path,
|
|
440
459
|
status,
|
|
441
|
-
collection
|
|
460
|
+
collection,
|
|
442
461
|
context,
|
|
443
|
-
currentEntityId: entityId,
|
|
444
462
|
formContext,
|
|
445
463
|
openEntityMode,
|
|
446
464
|
disabled: actionsDisabled,
|
|
@@ -621,11 +639,17 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
621
639
|
<>
|
|
622
640
|
{!Builder && <div className={"w-full py-2 flex flex-col items-start my-4 lg:my-6"}>
|
|
623
641
|
<Typography
|
|
624
|
-
className={"
|
|
642
|
+
className={"my-4 flex-grow line-clamp-1 " + (collection.hideIdFromForm ? "mb-6" : "")}
|
|
625
643
|
variant={"h4"}>
|
|
626
644
|
{title ?? collection.singularName ?? collection.name}
|
|
627
645
|
</Typography>
|
|
628
|
-
|
|
646
|
+
|
|
647
|
+
{!entity?.values && initialStatus === "existing" &&
|
|
648
|
+
<Alert color={"warning"} size={"small"} outerClassName={"w-full mb-4 text-xs"}>
|
|
649
|
+
This entity does not exist in the database
|
|
650
|
+
</Alert>}
|
|
651
|
+
|
|
652
|
+
{showEntityPath && <Alert color={"base"} outerClassName={"w-full"} size={"small"}>
|
|
629
653
|
<code
|
|
630
654
|
className={"text-xs select-all text-text-secondary dark:text-text-secondary-dark"}>
|
|
631
655
|
{entity?.path ?? path}/{entityId}
|
|
@@ -635,6 +659,10 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
635
659
|
|
|
636
660
|
{children}
|
|
637
661
|
|
|
662
|
+
{initialEntityId && !entity && initialStatus !== "new" && <Alert color={"info"} size={"small"}>
|
|
663
|
+
This entity does not exist in the database
|
|
664
|
+
</Alert>}
|
|
665
|
+
|
|
638
666
|
{!Builder && !collection.hideIdFromForm &&
|
|
639
667
|
<CustomIdField customId={collection.customId}
|
|
640
668
|
entityId={entityId}
|
|
@@ -668,6 +696,8 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
668
696
|
const dialogActions = <EntityFormActionsComponent
|
|
669
697
|
collection={resolvedCollection}
|
|
670
698
|
path={path}
|
|
699
|
+
fullPath={path}
|
|
700
|
+
fullIdPath={fullIdPath}
|
|
671
701
|
entity={entity}
|
|
672
702
|
layout={forceActionsAtTheBottom ? "bottom" : "side"}
|
|
673
703
|
savingError={savingError}
|
|
@@ -677,6 +707,8 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
677
707
|
pluginActions={pluginActions ?? []}
|
|
678
708
|
openEntityMode={openEntityMode}
|
|
679
709
|
showDefaultActions={showDefaultActions}
|
|
710
|
+
navigateBack={navigateBack}
|
|
711
|
+
formContext={formContext}
|
|
680
712
|
/>;
|
|
681
713
|
|
|
682
714
|
return (
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Entity,
|
|
4
|
+
EntityAction,
|
|
5
|
+
FireCMSContext,
|
|
6
|
+
FormContext,
|
|
7
|
+
ResolvedEntityCollection,
|
|
8
|
+
SideEntityController
|
|
9
|
+
} from "../types";
|
|
3
10
|
import { Button, cls, defaultBorderMixin, DialogActions, IconButton, LoadingButton, Typography } from "@firecms/ui";
|
|
4
11
|
import { FormexController } from "@firecms/formex";
|
|
5
12
|
import { useFireCMSContext, useSideEntityController } from "../hooks";
|
|
6
13
|
|
|
7
14
|
export interface EntityFormActionsProps {
|
|
15
|
+
fullPath: string;
|
|
16
|
+
fullIdPath?: string;
|
|
8
17
|
collection: ResolvedEntityCollection;
|
|
9
18
|
path: string;
|
|
10
19
|
entity?: Entity;
|
|
@@ -16,9 +25,13 @@ export interface EntityFormActionsProps {
|
|
|
16
25
|
pluginActions: React.ReactNode[];
|
|
17
26
|
openEntityMode: "side_panel" | "full_screen";
|
|
18
27
|
showDefaultActions?: boolean;
|
|
28
|
+
navigateBack: () => void;
|
|
29
|
+
formContext: FormContext
|
|
19
30
|
}
|
|
20
31
|
|
|
21
32
|
export function EntityFormActions({
|
|
33
|
+
fullPath,
|
|
34
|
+
fullIdPath,
|
|
22
35
|
collection,
|
|
23
36
|
entity,
|
|
24
37
|
layout,
|
|
@@ -27,7 +40,9 @@ export function EntityFormActions({
|
|
|
27
40
|
disabled,
|
|
28
41
|
status,
|
|
29
42
|
pluginActions,
|
|
30
|
-
openEntityMode
|
|
43
|
+
openEntityMode,
|
|
44
|
+
navigateBack,
|
|
45
|
+
formContext
|
|
31
46
|
}: EntityFormActionsProps) {
|
|
32
47
|
|
|
33
48
|
const context = useFireCMSContext();
|
|
@@ -35,6 +50,8 @@ export function EntityFormActions({
|
|
|
35
50
|
|
|
36
51
|
return layout === "bottom"
|
|
37
52
|
? buildBottomActions({
|
|
53
|
+
fullPath,
|
|
54
|
+
fullIdPath,
|
|
38
55
|
savingError,
|
|
39
56
|
entity,
|
|
40
57
|
collection,
|
|
@@ -44,9 +61,13 @@ export function EntityFormActions({
|
|
|
44
61
|
disabled,
|
|
45
62
|
status,
|
|
46
63
|
pluginActions,
|
|
47
|
-
openEntityMode
|
|
64
|
+
openEntityMode,
|
|
65
|
+
navigateBack,
|
|
66
|
+
formContext
|
|
48
67
|
})
|
|
49
68
|
: buildSideActions({
|
|
69
|
+
fullPath,
|
|
70
|
+
fullIdPath,
|
|
50
71
|
savingError,
|
|
51
72
|
entity,
|
|
52
73
|
collection,
|
|
@@ -56,11 +77,15 @@ export function EntityFormActions({
|
|
|
56
77
|
disabled,
|
|
57
78
|
status,
|
|
58
79
|
pluginActions,
|
|
59
|
-
openEntityMode
|
|
80
|
+
openEntityMode,
|
|
81
|
+
navigateBack,
|
|
82
|
+
formContext
|
|
60
83
|
});
|
|
61
84
|
}
|
|
62
85
|
|
|
63
86
|
type ActionsViewProps<M extends object> = {
|
|
87
|
+
fullPath: string,
|
|
88
|
+
fullIdPath?: string,
|
|
64
89
|
savingError: Error | undefined,
|
|
65
90
|
entity: Entity<M> | undefined,
|
|
66
91
|
formActions?: EntityAction[],
|
|
@@ -72,11 +97,15 @@ type ActionsViewProps<M extends object> = {
|
|
|
72
97
|
status: "new" | "existing" | "copy",
|
|
73
98
|
pluginActions?: React.ReactNode[],
|
|
74
99
|
openEntityMode: "side_panel" | "full_screen";
|
|
100
|
+
navigateBack: () => void;
|
|
101
|
+
formContext: FormContext
|
|
75
102
|
};
|
|
76
103
|
|
|
77
104
|
function buildBottomActions<M extends object>({
|
|
78
105
|
savingError,
|
|
79
106
|
entity,
|
|
107
|
+
fullPath,
|
|
108
|
+
fullIdPath,
|
|
80
109
|
formActions,
|
|
81
110
|
collection,
|
|
82
111
|
context,
|
|
@@ -85,7 +114,9 @@ function buildBottomActions<M extends object>({
|
|
|
85
114
|
disabled,
|
|
86
115
|
status,
|
|
87
116
|
pluginActions,
|
|
88
|
-
openEntityMode
|
|
117
|
+
openEntityMode,
|
|
118
|
+
navigateBack,
|
|
119
|
+
formContext
|
|
89
120
|
}: ActionsViewProps<M>) {
|
|
90
121
|
|
|
91
122
|
return <DialogActions position={"absolute"}>
|
|
@@ -103,12 +134,16 @@ function buildBottomActions<M extends object>({
|
|
|
103
134
|
event.stopPropagation();
|
|
104
135
|
if (entity)
|
|
105
136
|
action.onClick({
|
|
137
|
+
view: "form",
|
|
106
138
|
entity,
|
|
107
|
-
fullPath: collection.path,
|
|
139
|
+
fullPath: fullPath ?? collection.path,
|
|
140
|
+
fullIdPath: fullIdPath ?? collection.id,
|
|
108
141
|
collection: collection,
|
|
109
142
|
context,
|
|
110
143
|
sideEntityController,
|
|
111
|
-
openEntityMode: openEntityMode
|
|
144
|
+
openEntityMode: openEntityMode,
|
|
145
|
+
navigateBack,
|
|
146
|
+
formContext
|
|
112
147
|
});
|
|
113
148
|
}}>
|
|
114
149
|
{action.icon}
|
|
@@ -116,10 +151,14 @@ function buildBottomActions<M extends object>({
|
|
|
116
151
|
))}
|
|
117
152
|
</div>}
|
|
118
153
|
{pluginActions}
|
|
119
|
-
<Button variant="text" disabled={disabled || isSubmitting}
|
|
154
|
+
<Button variant="text" disabled={disabled || isSubmitting}
|
|
155
|
+
color={"primary"}
|
|
156
|
+
type="reset">
|
|
120
157
|
{status === "existing" ? "Discard" : "Clear"}
|
|
121
158
|
</Button>
|
|
122
|
-
<Button variant={"filled"}
|
|
159
|
+
<Button variant={"filled"}
|
|
160
|
+
color="primary"
|
|
161
|
+
type="submit"
|
|
123
162
|
disabled={disabled || isSubmitting}>
|
|
124
163
|
{status === "existing" && "Save"}
|
|
125
164
|
{status === "copy" && "Create copy"}
|
|
@@ -133,6 +172,9 @@ function buildSideActions<M extends object>({
|
|
|
133
172
|
savingError,
|
|
134
173
|
entity,
|
|
135
174
|
formActions,
|
|
175
|
+
fullPath,
|
|
176
|
+
fullIdPath,
|
|
177
|
+
openEntityMode,
|
|
136
178
|
collection,
|
|
137
179
|
context,
|
|
138
180
|
sideEntityController,
|
|
@@ -214,7 +214,6 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
|
|
|
214
214
|
|
|
215
215
|
const customFieldProps: any = property.customProps;
|
|
216
216
|
const value = formexFieldProps.field.value;
|
|
217
|
-
// const initialValue = formexFieldProps.meta.initialValue;
|
|
218
217
|
const error = getIn(formexFieldProps.form.errors, propertyKey);
|
|
219
218
|
const touched = getIn(formexFieldProps.form.touched, propertyKey);
|
|
220
219
|
|
|
@@ -103,7 +103,9 @@ export function CustomIdField<M extends Record<string, any>>({
|
|
|
103
103
|
size={"large"}
|
|
104
104
|
error={error}
|
|
105
105
|
fullWidth={true}
|
|
106
|
-
onValueChange={(v) =>
|
|
106
|
+
onValueChange={(v) => {
|
|
107
|
+
onChange(v as string);
|
|
108
|
+
}}
|
|
107
109
|
{...fieldProps}
|
|
108
110
|
renderValue={(option) => {
|
|
109
111
|
const enumConfig = enumValues.find(e => e.id === option);
|
|
@@ -24,7 +24,7 @@ export const LabelWithIcon = forwardRef<HTMLDivElement, LabelWithIconProps>(
|
|
|
24
24
|
return (
|
|
25
25
|
<div
|
|
26
26
|
ref={ref}
|
|
27
|
-
className={cls("inline-flex items-center my-0.5",
|
|
27
|
+
className={cls("align-middle inline-flex items-center my-0.5",
|
|
28
28
|
small ? "gap-1" : "gap-2",
|
|
29
29
|
className)}
|
|
30
30
|
>
|
|
@@ -30,7 +30,7 @@ export function StorageItemPreview({
|
|
|
30
30
|
|
|
31
31
|
return (
|
|
32
32
|
<div className={cls(paperMixin,
|
|
33
|
-
"relative
|
|
33
|
+
"relative border-box flex items-center justify-center",
|
|
34
34
|
size === "large" ? "min-w-[220px] min-h-[220px] max-w-[220px]" : "min-w-[118px] min-h-[118px] max-w-[118px]",
|
|
35
35
|
className)}>
|
|
36
36
|
|
|
@@ -79,7 +79,7 @@ export function StorageUploadProgress({
|
|
|
79
79
|
}, [entry.file, entry.fileName, upload]);
|
|
80
80
|
|
|
81
81
|
if (simple) {
|
|
82
|
-
return <div className={`
|
|
82
|
+
return <div className={`w-${imageSize} h-${imageSize}`}>
|
|
83
83
|
|
|
84
84
|
{loading && <Skeleton className={`w-${imageSize} h-${imageSize}`}/>}
|
|
85
85
|
|
|
@@ -88,11 +88,11 @@ export function StorageUploadProgress({
|
|
|
88
88
|
return (
|
|
89
89
|
|
|
90
90
|
<div className={cls(paperMixin,
|
|
91
|
-
"
|
|
91
|
+
"p-4 relative border-box flex items-center justify-center",
|
|
92
92
|
`min-w-[${imageSize}px] min-h-[${imageSize}px]`)}>
|
|
93
93
|
|
|
94
94
|
{loading &&
|
|
95
|
-
<Skeleton className="
|
|
95
|
+
<Skeleton className="w-full h-full"/>}
|
|
96
96
|
|
|
97
97
|
{error && <ErrorView title={"Error uploading file"}
|
|
98
98
|
error={error}/>}
|
|
@@ -6,7 +6,7 @@ import { ArrayContainer, ArrayEntryParams, ErrorView } from "../../components";
|
|
|
6
6
|
import { getIconForProperty, getReferenceFrom } from "../../util";
|
|
7
7
|
|
|
8
8
|
import { useNavigationController, useReferenceDialog } from "../../hooks";
|
|
9
|
-
import { Button, cls, ExpandablePanel, fieldBackgroundMixin, Typography } from "@firecms/ui";
|
|
9
|
+
import { Button, cls, EditIcon, ExpandablePanel, fieldBackgroundMixin, Typography } from "@firecms/ui";
|
|
10
10
|
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
11
11
|
|
|
12
12
|
type ArrayOfReferencesFieldProps = FieldProps<EntityReference[]>;
|
|
@@ -127,11 +127,12 @@ export function ArrayOfReferencesFieldBinding({
|
|
|
127
127
|
/>
|
|
128
128
|
|
|
129
129
|
<Button
|
|
130
|
-
className="my-4 justify-center text-left"
|
|
130
|
+
className="ml-3.5 my-4 justify-center text-left"
|
|
131
131
|
variant="text"
|
|
132
132
|
color="primary"
|
|
133
133
|
disabled={isSubmitting}
|
|
134
134
|
onClick={onEntryClick}>
|
|
135
|
+
<EditIcon size={"small"}/>
|
|
135
136
|
Edit {property.name}
|
|
136
137
|
</Button>
|
|
137
138
|
</div>}
|
|
@@ -21,6 +21,7 @@ export function MapFieldBinding({
|
|
|
21
21
|
error,
|
|
22
22
|
disabled,
|
|
23
23
|
property,
|
|
24
|
+
partOfArray,
|
|
24
25
|
minimalistView: minimalistViewProp,
|
|
25
26
|
includeDescription,
|
|
26
27
|
autoFocus,
|
|
@@ -51,7 +52,8 @@ export function MapFieldBinding({
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
const mapFormView = <>
|
|
54
|
-
<div
|
|
55
|
+
<div
|
|
56
|
+
className={cls("py-1 flex flex-col space-y-2", minimalistView && property.widthPercentage !== undefined ? "mt-8" : undefined)}>
|
|
55
57
|
{Object.entries(mapProperties)
|
|
56
58
|
.filter(([_, property]) => !isHidden(property))
|
|
57
59
|
.map(([entryKey, childProperty], index) => {
|
|
@@ -81,8 +83,7 @@ export function MapFieldBinding({
|
|
|
81
83
|
{...fieldBindingProps}/>
|
|
82
84
|
</ErrorBoundary>
|
|
83
85
|
</div>
|
|
84
|
-
)
|
|
85
|
-
;
|
|
86
|
+
) ;
|
|
86
87
|
}
|
|
87
88
|
)
|
|
88
89
|
}
|
|
@@ -117,7 +118,7 @@ export function MapFieldBinding({
|
|
|
117
118
|
|
|
118
119
|
<FieldHelperText includeDescription={includeDescription}
|
|
119
120
|
showError={showError ?? false}
|
|
120
|
-
error={error ? (typeof error === "string" ? error : "A property of this map has an error") : undefined}
|
|
121
|
+
error={error && !partOfArray ? (typeof error === "string" ? error : "A property of this map has an error") : undefined}
|
|
121
122
|
disabled={disabled}
|
|
122
123
|
property={property}/>
|
|
123
124
|
|
|
@@ -46,11 +46,13 @@ export function MarkdownEditorFieldBinding({
|
|
|
46
46
|
const entityId = context.entityId;
|
|
47
47
|
const path = context.path;
|
|
48
48
|
|
|
49
|
-
// const fieldVersion = useRef(0);
|
|
50
49
|
const [fieldVersion, setFieldVersion] = useState(0);
|
|
51
|
-
const internalValue = useRef(value);
|
|
50
|
+
const internalValue = useRef<string | null>(value);
|
|
52
51
|
|
|
53
52
|
const onContentChange = useCallback((content: string) => {
|
|
53
|
+
if (content === value || (value === null && content === "")) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
54
56
|
internalValue.current = content;
|
|
55
57
|
setValue(content);
|
|
56
58
|
}, [setValue]);
|
|
@@ -84,7 +84,7 @@ export function MultiSelectFieldBinding({
|
|
|
84
84
|
<MultiSelect
|
|
85
85
|
className={"w-full mt-2"}
|
|
86
86
|
size={size}
|
|
87
|
-
value={validValue ? value.map((v) => v
|
|
87
|
+
value={validValue ? value.map((v) => v?.toString()) : []}
|
|
88
88
|
disabled={disabled}
|
|
89
89
|
modalPopover={true}
|
|
90
90
|
label={<LabelWithIconAndTooltip
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import React, { useCallback, useMemo } from "react";
|
|
2
|
+
import { Entity, EntityCollection, EntityReference, FieldProps } from "../../types";
|
|
3
|
+
import { useNavigationController, useReferenceDialog } from "../../hooks";
|
|
4
|
+
import { ReadOnlyFieldBinding } from "./ReadOnlyFieldBinding";
|
|
5
|
+
import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
|
|
6
|
+
import { ErrorView } from "../../components";
|
|
7
|
+
import { ReferencePreview } from "../../preview";
|
|
8
|
+
import { getIconForProperty, IconForView } from "../../util";
|
|
9
|
+
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
10
|
+
import { EntityPreviewContainer } from "../../components/EntityPreview";
|
|
11
|
+
import { cls } from "@firecms/ui";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Field that opens a reference selection dialog and stores the entity ID as a string.
|
|
15
|
+
*
|
|
16
|
+
* This is one of the internal components that get mapped natively inside forms
|
|
17
|
+
* and tables to the specified properties.
|
|
18
|
+
* @group Form fields
|
|
19
|
+
*/
|
|
20
|
+
export function ReferenceAsStringFieldBinding(props: FieldProps<string>) {
|
|
21
|
+
if (typeof props.property.reference?.path !== "string") {
|
|
22
|
+
return <ReadOnlyFieldBinding {...props}/>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return <ReferenceAsStringFieldBindingInternal {...props}/>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function ReferenceAsStringFieldBindingInternal({
|
|
29
|
+
propertyKey,
|
|
30
|
+
value,
|
|
31
|
+
setValue,
|
|
32
|
+
error,
|
|
33
|
+
showError,
|
|
34
|
+
isSubmitting,
|
|
35
|
+
disabled,
|
|
36
|
+
minimalistView,
|
|
37
|
+
property,
|
|
38
|
+
includeDescription,
|
|
39
|
+
size = "medium"
|
|
40
|
+
}: FieldProps<string>) {
|
|
41
|
+
if (!property.reference?.path) {
|
|
42
|
+
throw new Error("Property path is required for ReferenceAsStringFieldBinding");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
useClearRestoreValue({
|
|
46
|
+
property,
|
|
47
|
+
value,
|
|
48
|
+
setValue
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const navigationController = useNavigationController();
|
|
52
|
+
const path = property.reference.path;
|
|
53
|
+
const collection: EntityCollection | undefined = useMemo(() => {
|
|
54
|
+
return path ? navigationController.getCollection(path) : undefined;
|
|
55
|
+
}, [path]);
|
|
56
|
+
|
|
57
|
+
const referenceValue: EntityReference | undefined = useMemo(() => {
|
|
58
|
+
if (value && path) {
|
|
59
|
+
return new EntityReference(value, path);
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}, [value, path]);
|
|
63
|
+
|
|
64
|
+
if (!collection) {
|
|
65
|
+
throw Error(`Couldn't find the corresponding collection for the path: ${path}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const onSingleEntitySelected = useCallback((e: Entity<any> | null) => {
|
|
69
|
+
setValue(e ? e.id : null);
|
|
70
|
+
}, [setValue]);
|
|
71
|
+
|
|
72
|
+
const referenceDialogController = useReferenceDialog({
|
|
73
|
+
multiselect: false,
|
|
74
|
+
path: path,
|
|
75
|
+
collection,
|
|
76
|
+
onSingleEntitySelected,
|
|
77
|
+
selectedEntityIds: value ? [value] : undefined,
|
|
78
|
+
forceFilter: property.reference.forceFilter
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const onEntryClick = (e: React.SyntheticEvent) => {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
referenceDialogController.open();
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
{!minimalistView && <LabelWithIconAndTooltip
|
|
90
|
+
propertyKey={propertyKey}
|
|
91
|
+
icon={getIconForProperty(property, "small")}
|
|
92
|
+
required={property.validation?.required}
|
|
93
|
+
title={property.name}
|
|
94
|
+
className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
|
|
95
|
+
|
|
96
|
+
{!collection && <ErrorView
|
|
97
|
+
error={"The specified collection does not exist. Check console"}/>}
|
|
98
|
+
|
|
99
|
+
{collection && <>
|
|
100
|
+
|
|
101
|
+
{referenceValue && <ReferencePreview
|
|
102
|
+
disabled={!path}
|
|
103
|
+
previewProperties={property.reference?.previewProperties}
|
|
104
|
+
hover={!disabled}
|
|
105
|
+
size={size}
|
|
106
|
+
onClick={disabled || isSubmitting ? undefined : onEntryClick}
|
|
107
|
+
reference={referenceValue}
|
|
108
|
+
includeEntityLink={property.reference?.includeEntityLink}
|
|
109
|
+
includeId={property.reference?.includeId}
|
|
110
|
+
/>}
|
|
111
|
+
|
|
112
|
+
{!value && <div className="justify-center text-left">
|
|
113
|
+
<EntityPreviewContainer
|
|
114
|
+
className={cls("px-6 h-16 text-sm font-medium flex items-center gap-6",
|
|
115
|
+
disabled || isSubmitting
|
|
116
|
+
? "text-surface-accent-500"
|
|
117
|
+
: "cursor-pointer text-surface-accent-700 dark:text-surface-accent-300 hover:bg-surface-accent-50 dark:hover:bg-surface-800 group-hover:bg-surface-accent-50 dark:group-hover:bg-surface-800")}
|
|
118
|
+
onClick={onEntryClick}
|
|
119
|
+
size={"medium"}>
|
|
120
|
+
<IconForView collectionOrView={collection}
|
|
121
|
+
className={"text-surface-300 dark:text-surface-600"}/>
|
|
122
|
+
{`Edit ${property.name}`.toUpperCase()}
|
|
123
|
+
</EntityPreviewContainer>
|
|
124
|
+
</div>}
|
|
125
|
+
</>}
|
|
126
|
+
|
|
127
|
+
<FieldHelperText includeDescription={includeDescription}
|
|
128
|
+
showError={showError}
|
|
129
|
+
error={error}
|
|
130
|
+
disabled={disabled}
|
|
131
|
+
property={property}/>
|
|
132
|
+
|
|
133
|
+
</>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
@@ -67,8 +67,9 @@ export function SelectFieldBinding<T extends EnumType>({
|
|
|
67
67
|
</PropertyIdCopyTooltip>}
|
|
68
68
|
endAdornment={
|
|
69
69
|
property.clearable && !disabled && <IconButton
|
|
70
|
+
size="small"
|
|
70
71
|
onClick={handleClearClick}>
|
|
71
|
-
<CloseIcon/>
|
|
72
|
+
<CloseIcon size={"small"}/>
|
|
72
73
|
</IconButton>
|
|
73
74
|
}
|
|
74
75
|
onValueChange={(updatedValue: string) => {
|