@firecms/core 3.0.0-canary.190 → 3.0.0-canary.191
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/core/EntityEditView.d.ts +1 -2
- package/dist/core/EntityEditViewFormActions.d.ts +2 -0
- package/dist/core/SideDialogs.d.ts +4 -2
- package/dist/{core → form}/EntityForm.d.ts +6 -4
- package/dist/form/EntityFormActions.d.ts +16 -0
- package/dist/form/index.d.ts +16 -16
- package/dist/index.es.js +2391 -2292
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2783 -2684
- package/dist/index.umd.js.map +1 -1
- package/dist/types/fields.d.ts +0 -6
- package/package.json +5 -5
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +2 -10
- package/src/core/EntityEditView.tsx +14 -85
- package/src/core/EntityEditViewFormActions.tsx +198 -0
- package/src/core/EntitySidePanel.tsx +8 -1
- package/src/core/SideDialogs.tsx +16 -12
- package/src/{core → form}/EntityForm.tsx +37 -243
- package/src/form/EntityFormActions.tsx +168 -0
- package/src/form/index.tsx +16 -32
- package/src/internal/useBuildSideEntityController.tsx +0 -52
- package/src/types/fields.tsx +0 -7
|
@@ -2,29 +2,23 @@ import React, { useCallback, useDeferredValue, useEffect, useMemo, useRef, useSt
|
|
|
2
2
|
import {
|
|
3
3
|
CMSAnalyticsEvent,
|
|
4
4
|
Entity,
|
|
5
|
-
EntityAction,
|
|
6
5
|
EntityCollection,
|
|
7
6
|
EntityStatus,
|
|
8
7
|
EntityValues,
|
|
9
|
-
FireCMSContext,
|
|
10
8
|
FormContext,
|
|
11
9
|
PluginFormActionProps,
|
|
12
10
|
PropertyFieldBindingProps,
|
|
13
|
-
ResolvedEntityCollection
|
|
14
|
-
SideEntityController
|
|
11
|
+
ResolvedEntityCollection
|
|
15
12
|
} from "../types";
|
|
16
13
|
import equal from "react-fast-compare";
|
|
17
14
|
|
|
18
|
-
import {
|
|
15
|
+
import { ErrorBoundary, getFormFieldKeys } from "../components";
|
|
19
16
|
import {
|
|
20
|
-
canCreateEntity,
|
|
21
|
-
canDeleteEntity,
|
|
22
17
|
getDefaultValuesFor,
|
|
23
18
|
getEntityTitlePropertyKey,
|
|
24
19
|
getValueInPath,
|
|
25
20
|
isHidden,
|
|
26
21
|
isReadOnly,
|
|
27
|
-
mergeEntityActions,
|
|
28
22
|
resolveCollection,
|
|
29
23
|
useDebouncedCallback
|
|
30
24
|
} from "../util";
|
|
@@ -34,26 +28,11 @@ import {
|
|
|
34
28
|
useAuthController,
|
|
35
29
|
useCustomizationController,
|
|
36
30
|
useDataSource,
|
|
37
|
-
useFireCMSContext,
|
|
31
|
+
useFireCMSContext, useSideDialogsController,
|
|
38
32
|
useSideEntityController,
|
|
39
33
|
useSnackbarController
|
|
40
34
|
} from "../hooks";
|
|
41
|
-
import {
|
|
42
|
-
Alert,
|
|
43
|
-
Button,
|
|
44
|
-
CheckIcon,
|
|
45
|
-
Chip,
|
|
46
|
-
cls,
|
|
47
|
-
defaultBorderMixin,
|
|
48
|
-
DialogActions,
|
|
49
|
-
EditIcon,
|
|
50
|
-
IconButton,
|
|
51
|
-
LoadingButton,
|
|
52
|
-
NotesIcon,
|
|
53
|
-
paperMixin,
|
|
54
|
-
Tooltip,
|
|
55
|
-
Typography
|
|
56
|
-
} from "@firecms/ui";
|
|
35
|
+
import { Alert, CheckIcon, Chip, cls, EditIcon, NotesIcon, paperMixin, Tooltip, Typography } from "@firecms/ui";
|
|
57
36
|
import { Formex, FormexController, getIn, setIn, useCreateFormex } from "@firecms/formex";
|
|
58
37
|
import { useAnalyticsController } from "../hooks/useAnalyticsController";
|
|
59
38
|
import { FormEntry, FormLayout, LabelWithIconAndTooltip, PropertyFieldBinding } from "../form";
|
|
@@ -62,6 +41,8 @@ import { removeEntityFromCache, saveEntityToCache } from "../util/entity_cache";
|
|
|
62
41
|
import { CustomIdField } from "../form/components/CustomIdField";
|
|
63
42
|
import { ErrorFocus } from "../form/components/ErrorFocus";
|
|
64
43
|
import { CustomFieldValidator, getYupEntitySchema } from "../form/validation";
|
|
44
|
+
import { EntityFormActions, EntityFormActionsProps } from "./EntityFormActions";
|
|
45
|
+
import { useSideDialogContext } from "../core";
|
|
65
46
|
|
|
66
47
|
export type OnUpdateParams = {
|
|
67
48
|
entity: Entity<any>,
|
|
@@ -81,16 +62,16 @@ type EntityFormProps<M extends Record<string, any>> = {
|
|
|
81
62
|
onIdChange?: (id: string) => void;
|
|
82
63
|
onValuesModified?: (modified: boolean) => void;
|
|
83
64
|
onSaved?: (params: OnUpdateParams) => void;
|
|
84
|
-
onClose?: () => void;
|
|
85
65
|
cachedDirtyValues?: Partial<M>; // dirty cached entity in memory
|
|
86
66
|
onFormContextReady?: (formContext: FormContext) => void;
|
|
87
67
|
forceActionsAtTheBottom?: boolean;
|
|
88
|
-
initialStatus: EntityStatus;
|
|
89
68
|
className?: string;
|
|
69
|
+
initialStatus: EntityStatus;
|
|
90
70
|
onStatusChange?: (status: EntityStatus) => void;
|
|
91
71
|
onEntityChange?: (entity: Entity<M>) => void;
|
|
92
72
|
formex?: FormexController<M>;
|
|
93
73
|
openEntityMode?: "side_panel" | "full_screen";
|
|
74
|
+
EntityFormActionsComponent?: React.FC<EntityFormActionsProps>;
|
|
94
75
|
};
|
|
95
76
|
|
|
96
77
|
export function EntityForm<M extends Record<string, any>>({
|
|
@@ -100,7 +81,6 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
100
81
|
onValuesModified,
|
|
101
82
|
onIdChange,
|
|
102
83
|
onSaved,
|
|
103
|
-
onClose,
|
|
104
84
|
entity,
|
|
105
85
|
cachedDirtyValues,
|
|
106
86
|
onFormContextReady,
|
|
@@ -110,7 +90,8 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
110
90
|
onStatusChange,
|
|
111
91
|
onEntityChange,
|
|
112
92
|
openEntityMode = "full_screen",
|
|
113
|
-
formex: formexProp
|
|
93
|
+
formex: formexProp,
|
|
94
|
+
EntityFormActionsComponent = EntityFormActions
|
|
114
95
|
}: EntityFormProps<M>) {
|
|
115
96
|
|
|
116
97
|
if (collection.customId && collection.formAutoSave) {
|
|
@@ -133,17 +114,13 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
133
114
|
collection,
|
|
134
115
|
path,
|
|
135
116
|
values: valuesToBeSaved,
|
|
136
|
-
closeAfterSave: false
|
|
137
117
|
});
|
|
138
118
|
}, false, 2000);
|
|
139
119
|
|
|
140
|
-
const authController = useAuthController();
|
|
141
120
|
const dataSource = useDataSource(collection);
|
|
142
|
-
const sideEntityController = useSideEntityController();
|
|
143
121
|
const snackbarController = useSnackbarController();
|
|
144
122
|
const customizationController = useCustomizationController();
|
|
145
123
|
const context = useFireCMSContext();
|
|
146
|
-
const closeAfterSaveRef = useRef(false);
|
|
147
124
|
const analyticsController = useAnalyticsController();
|
|
148
125
|
|
|
149
126
|
const [underlyingChanges, setUnderlyingChanges] = useState<Partial<EntityValues<M>>>({});
|
|
@@ -262,7 +239,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
262
239
|
}
|
|
263
240
|
}
|
|
264
241
|
|
|
265
|
-
const onSaveSuccess = (updatedEntity: Entity<M
|
|
242
|
+
const onSaveSuccess = (updatedEntity: Entity<M>) => {
|
|
266
243
|
|
|
267
244
|
clearDirtyCache();
|
|
268
245
|
onValuesModified?.(false);
|
|
@@ -285,10 +262,6 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
285
262
|
collection
|
|
286
263
|
});
|
|
287
264
|
}
|
|
288
|
-
|
|
289
|
-
if (closeAfterSave) {
|
|
290
|
-
onClose?.();
|
|
291
|
-
}
|
|
292
265
|
};
|
|
293
266
|
|
|
294
267
|
const onSaveFailure = useCallback((e: Error) => {
|
|
@@ -304,7 +277,6 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
304
277
|
const saveEntity = ({
|
|
305
278
|
values,
|
|
306
279
|
previousValues,
|
|
307
|
-
closeAfterSave,
|
|
308
280
|
entityId,
|
|
309
281
|
collection,
|
|
310
282
|
path
|
|
@@ -314,7 +286,6 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
314
286
|
entityId: string | undefined,
|
|
315
287
|
values: M,
|
|
316
288
|
previousValues?: M,
|
|
317
|
-
closeAfterSave: boolean,
|
|
318
289
|
}) => {
|
|
319
290
|
setSaving(true);
|
|
320
291
|
return saveEntityWithCallbacks({
|
|
@@ -326,7 +297,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
326
297
|
status,
|
|
327
298
|
dataSource,
|
|
328
299
|
context,
|
|
329
|
-
onSaveSuccess: (updatedEntity: Entity<M>) => onSaveSuccess(updatedEntity
|
|
300
|
+
onSaveSuccess: (updatedEntity: Entity<M>) => onSaveSuccess(updatedEntity),
|
|
330
301
|
onSaveFailure,
|
|
331
302
|
onPreSaveHookError,
|
|
332
303
|
onSaveSuccessHookError
|
|
@@ -339,7 +310,6 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
339
310
|
entityId: string | undefined,
|
|
340
311
|
values: EntityValues<M>,
|
|
341
312
|
previousValues?: EntityValues<M>,
|
|
342
|
-
closeAfterSave: boolean,
|
|
343
313
|
autoSave: boolean
|
|
344
314
|
};
|
|
345
315
|
|
|
@@ -349,7 +319,6 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
349
319
|
entityId,
|
|
350
320
|
values,
|
|
351
321
|
previousValues,
|
|
352
|
-
closeAfterSave,
|
|
353
322
|
autoSave
|
|
354
323
|
}: EntityFormSaveParams<M>): Promise<void> => {
|
|
355
324
|
if (!status)
|
|
@@ -363,7 +332,6 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
363
332
|
entityId,
|
|
364
333
|
values,
|
|
365
334
|
previousValues,
|
|
366
|
-
closeAfterSave
|
|
367
335
|
});
|
|
368
336
|
}
|
|
369
337
|
};
|
|
@@ -377,9 +345,8 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
377
345
|
entityId,
|
|
378
346
|
values,
|
|
379
347
|
previousValues: entity?.values,
|
|
380
|
-
closeAfterSave: closeAfterSaveRef.current,
|
|
381
348
|
autoSave: autoSave ?? false
|
|
382
|
-
}).then(
|
|
349
|
+
}).then((res) => {
|
|
383
350
|
const eventName: CMSAnalyticsEvent = status === "new"
|
|
384
351
|
? "new_entity_saved"
|
|
385
352
|
: (status === "copy" ? "entity_copied" : (status === "existing" ? "entity_edited" : "unmapped_event"));
|
|
@@ -387,8 +354,6 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
387
354
|
}).catch(e => {
|
|
388
355
|
console.error(e);
|
|
389
356
|
setSavingError(e);
|
|
390
|
-
}).finally(() => {
|
|
391
|
-
closeAfterSaveRef.current = false;
|
|
392
357
|
});
|
|
393
358
|
};
|
|
394
359
|
|
|
@@ -404,10 +369,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
404
369
|
entity,
|
|
405
370
|
savingError,
|
|
406
371
|
status,
|
|
407
|
-
openEntityMode
|
|
408
|
-
setPendingClose: (value: boolean) => {
|
|
409
|
-
closeAfterSaveRef.current = value;
|
|
410
|
-
}
|
|
372
|
+
openEntityMode
|
|
411
373
|
};
|
|
412
374
|
|
|
413
375
|
useEffect(() => {
|
|
@@ -645,6 +607,7 @@ export function EntityForm<M extends Record<string, any>>({
|
|
|
645
607
|
id={`form_${path}`}
|
|
646
608
|
pluginActions={pluginActions}
|
|
647
609
|
forceActionsAtTheBottom={forceActionsAtTheBottom}
|
|
610
|
+
EntityFormActionsComponent={EntityFormActionsComponent}
|
|
648
611
|
formContext={formContext}>
|
|
649
612
|
{formView}
|
|
650
613
|
</FormLayoutInner>
|
|
@@ -691,26 +654,24 @@ export function yupToFormErrors(yupError: ValidationError): Record<string, any>
|
|
|
691
654
|
return errors;
|
|
692
655
|
}
|
|
693
656
|
|
|
694
|
-
export function FormLayoutInner
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
657
|
+
export function FormLayoutInner({
|
|
658
|
+
id,
|
|
659
|
+
formContext,
|
|
660
|
+
children,
|
|
661
|
+
className,
|
|
662
|
+
forceActionsAtTheBottom,
|
|
663
|
+
pluginActions,
|
|
664
|
+
EntityFormActionsComponent
|
|
665
|
+
}: {
|
|
702
666
|
id?: string,
|
|
703
667
|
formContext: FormContext,
|
|
704
668
|
children: React.ReactNode,
|
|
705
669
|
className?: string,
|
|
706
670
|
forceActionsAtTheBottom?: boolean,
|
|
707
671
|
pluginActions?: React.ReactNode[],
|
|
672
|
+
EntityFormActionsComponent: React.FC<EntityFormActionsProps>;
|
|
708
673
|
}) {
|
|
709
674
|
|
|
710
|
-
const context = useFireCMSContext();
|
|
711
|
-
const sideEntityController = useSideEntityController();
|
|
712
|
-
const authController = useAuthController();
|
|
713
|
-
|
|
714
675
|
const formex = formContext.formex;
|
|
715
676
|
const collection = formContext.collection;
|
|
716
677
|
const path = formContext.path;
|
|
@@ -718,67 +679,24 @@ export function FormLayoutInner<M extends object>({
|
|
|
718
679
|
const savingError = formContext.savingError;
|
|
719
680
|
const status = formContext.status;
|
|
720
681
|
const openEntityMode = formContext.openEntityMode;
|
|
721
|
-
const setPendingClose = formContext.setPendingClose;
|
|
722
682
|
const disabled = formex.isSubmitting || (!formex.dirty && status === "existing");
|
|
723
683
|
|
|
724
684
|
if (!collection || !path) {
|
|
725
685
|
throw Error("INTERNAL: Collection and path must be defined in form context");
|
|
726
686
|
}
|
|
727
687
|
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
actions.push(copyEntityAction);
|
|
741
|
-
if (deleteEnabled)
|
|
742
|
-
actions.push(deleteEntityAction);
|
|
743
|
-
if (customEntityActions)
|
|
744
|
-
return mergeEntityActions(actions, customEntityActions);
|
|
745
|
-
return actions;
|
|
746
|
-
}, [authController, collection, path]);
|
|
747
|
-
|
|
748
|
-
const entityActions = getActionsForEntity({
|
|
749
|
-
entity,
|
|
750
|
-
customEntityActions: collection.entityActions
|
|
751
|
-
});
|
|
752
|
-
const formActions = entityActions.filter(a => a.includeInForm === undefined || a.includeInForm);
|
|
753
|
-
|
|
754
|
-
const dialogActions = forceActionsAtTheBottom
|
|
755
|
-
? buildBottomActions({
|
|
756
|
-
savingError,
|
|
757
|
-
entity,
|
|
758
|
-
formActions,
|
|
759
|
-
collection,
|
|
760
|
-
context,
|
|
761
|
-
sideEntityController,
|
|
762
|
-
isSubmitting: formex.isSubmitting,
|
|
763
|
-
disabled,
|
|
764
|
-
status,
|
|
765
|
-
setPendingClose,
|
|
766
|
-
pluginActions,
|
|
767
|
-
openEntityMode
|
|
768
|
-
})
|
|
769
|
-
: buildSideActions({
|
|
770
|
-
savingError,
|
|
771
|
-
entity,
|
|
772
|
-
formActions,
|
|
773
|
-
collection,
|
|
774
|
-
context,
|
|
775
|
-
sideEntityController,
|
|
776
|
-
isSubmitting: formex.isSubmitting,
|
|
777
|
-
disabled,
|
|
778
|
-
status,
|
|
779
|
-
pluginActions,
|
|
780
|
-
openEntityMode
|
|
781
|
-
});
|
|
688
|
+
const dialogActions = <EntityFormActionsComponent
|
|
689
|
+
collection={collection}
|
|
690
|
+
path={path}
|
|
691
|
+
entity={entity}
|
|
692
|
+
layout={forceActionsAtTheBottom ? "bottom" : "side"}
|
|
693
|
+
savingError={savingError}
|
|
694
|
+
formex={formex}
|
|
695
|
+
disabled={disabled}
|
|
696
|
+
status={status}
|
|
697
|
+
pluginActions={pluginActions ?? []}
|
|
698
|
+
openEntityMode={openEntityMode}
|
|
699
|
+
/>;
|
|
782
700
|
|
|
783
701
|
return (
|
|
784
702
|
<Formex value={formContext.formex}>
|
|
@@ -820,130 +738,6 @@ export function FormLayoutInner<M extends object>({
|
|
|
820
738
|
);
|
|
821
739
|
}
|
|
822
740
|
|
|
823
|
-
type ActionsViewProps<M extends object> = {
|
|
824
|
-
savingError: Error | undefined,
|
|
825
|
-
entity: Entity<M> | undefined,
|
|
826
|
-
formActions: EntityAction[],
|
|
827
|
-
collection: ResolvedEntityCollection,
|
|
828
|
-
context: FireCMSContext,
|
|
829
|
-
sideEntityController: SideEntityController,
|
|
830
|
-
isSubmitting: boolean,
|
|
831
|
-
disabled: boolean,
|
|
832
|
-
status: "new" | "existing" | "copy",
|
|
833
|
-
setPendingClose?: (value: boolean) => void,
|
|
834
|
-
pluginActions?: React.ReactNode[],
|
|
835
|
-
openEntityMode: "side_panel" | "full_screen";
|
|
836
|
-
};
|
|
837
|
-
|
|
838
|
-
function buildBottomActions<M extends object>({
|
|
839
|
-
savingError,
|
|
840
|
-
entity,
|
|
841
|
-
formActions,
|
|
842
|
-
collection,
|
|
843
|
-
context,
|
|
844
|
-
sideEntityController,
|
|
845
|
-
isSubmitting,
|
|
846
|
-
disabled,
|
|
847
|
-
status,
|
|
848
|
-
setPendingClose,
|
|
849
|
-
pluginActions,
|
|
850
|
-
openEntityMode
|
|
851
|
-
}: ActionsViewProps<M>) {
|
|
852
|
-
|
|
853
|
-
const canClose = openEntityMode === "side_panel";
|
|
854
|
-
return <DialogActions position={"absolute"}>
|
|
855
|
-
{savingError &&
|
|
856
|
-
<div className="text-right">
|
|
857
|
-
<Typography color={"error"}>{savingError.message}</Typography>
|
|
858
|
-
</div>
|
|
859
|
-
}
|
|
860
|
-
{entity && formActions.length > 0 && <div className="flex-grow flex overflow-auto no-scrollbar">
|
|
861
|
-
{formActions.map(action => (
|
|
862
|
-
<IconButton
|
|
863
|
-
key={action.name}
|
|
864
|
-
color="primary"
|
|
865
|
-
onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
|
866
|
-
event.stopPropagation();
|
|
867
|
-
if (entity)
|
|
868
|
-
action.onClick({
|
|
869
|
-
entity,
|
|
870
|
-
fullPath: collection.path,
|
|
871
|
-
collection: collection,
|
|
872
|
-
context,
|
|
873
|
-
sideEntityController,
|
|
874
|
-
openEntityMode: openEntityMode
|
|
875
|
-
});
|
|
876
|
-
}}>
|
|
877
|
-
{action.icon}
|
|
878
|
-
</IconButton>
|
|
879
|
-
))}
|
|
880
|
-
</div>}
|
|
881
|
-
{pluginActions}
|
|
882
|
-
<Button variant="text" disabled={disabled || isSubmitting} type="reset">
|
|
883
|
-
{status === "existing" ? "Discard" : "Clear"}
|
|
884
|
-
</Button>
|
|
885
|
-
<Button variant={canClose ? "text" : "filled"} color="primary" type="submit"
|
|
886
|
-
disabled={disabled || isSubmitting}
|
|
887
|
-
onClick={() => {
|
|
888
|
-
setPendingClose?.(false);
|
|
889
|
-
}}>
|
|
890
|
-
{status === "existing" && "Save"}
|
|
891
|
-
{status === "copy" && "Create copy"}
|
|
892
|
-
{status === "new" && "Create"}
|
|
893
|
-
</Button>
|
|
894
|
-
{canClose && <LoadingButton variant="filled"
|
|
895
|
-
color="primary"
|
|
896
|
-
type="submit"
|
|
897
|
-
loading={isSubmitting}
|
|
898
|
-
disabled={disabled}
|
|
899
|
-
onClick={() => {
|
|
900
|
-
setPendingClose?.(true);
|
|
901
|
-
}}>
|
|
902
|
-
{status === "existing" && "Save and close"}
|
|
903
|
-
{status === "copy" && "Create copy and close"}
|
|
904
|
-
{status === "new" && "Create and close"}
|
|
905
|
-
</LoadingButton>}
|
|
906
|
-
</DialogActions>;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
function buildSideActions<M extends object>({
|
|
910
|
-
savingError,
|
|
911
|
-
entity,
|
|
912
|
-
formActions,
|
|
913
|
-
collection,
|
|
914
|
-
context,
|
|
915
|
-
sideEntityController,
|
|
916
|
-
isSubmitting,
|
|
917
|
-
disabled,
|
|
918
|
-
status,
|
|
919
|
-
setPendingClose,
|
|
920
|
-
pluginActions
|
|
921
|
-
}: ActionsViewProps<M>) {
|
|
922
|
-
|
|
923
|
-
return <div
|
|
924
|
-
className={cls("overflow-auto h-full flex flex-col gap-2 w-80 2xl:w-96 px-4 py-16 sticky top-0 border-l", defaultBorderMixin)}>
|
|
925
|
-
<LoadingButton fullWidth={true} variant="filled" color="primary" type="submit" size={"large"}
|
|
926
|
-
disabled={disabled || isSubmitting} onClick={() => {
|
|
927
|
-
setPendingClose?.(false);
|
|
928
|
-
}}>
|
|
929
|
-
{status === "existing" && "Save"}
|
|
930
|
-
{status === "copy" && "Create copy"}
|
|
931
|
-
{status === "new" && "Create"}
|
|
932
|
-
</LoadingButton>
|
|
933
|
-
<Button fullWidth={true} variant="text" disabled={disabled || isSubmitting} type="reset">
|
|
934
|
-
{status === "existing" ? "Discard" : "Clear"}
|
|
935
|
-
</Button>
|
|
936
|
-
|
|
937
|
-
{pluginActions}
|
|
938
|
-
|
|
939
|
-
{savingError &&
|
|
940
|
-
<div className="text-right">
|
|
941
|
-
<Typography color={"error"}>{savingError.message}</Typography>
|
|
942
|
-
</div>
|
|
943
|
-
}
|
|
944
|
-
</div>;
|
|
945
|
-
}
|
|
946
|
-
|
|
947
741
|
function useOnAutoSave(autoSave: undefined | boolean, formex: FormexController<any>, lastSavedValues: any, save: (values: EntityValues<any>) => Promise<void>) {
|
|
948
742
|
if (!autoSave) return;
|
|
949
743
|
useEffect(() => {
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Entity, EntityAction, FireCMSContext, ResolvedEntityCollection, SideEntityController } from "../types";
|
|
3
|
+
import { Button, cls, defaultBorderMixin, DialogActions, IconButton, LoadingButton, Typography } from "@firecms/ui";
|
|
4
|
+
import { FormexController } from "@firecms/formex";
|
|
5
|
+
import { useFireCMSContext, useSideEntityController } from "../hooks";
|
|
6
|
+
|
|
7
|
+
export interface EntityFormActionsProps {
|
|
8
|
+
collection: ResolvedEntityCollection;
|
|
9
|
+
path: string;
|
|
10
|
+
entity?: Entity;
|
|
11
|
+
layout: "bottom" | "side";
|
|
12
|
+
savingError?: Error;
|
|
13
|
+
formex: FormexController<any>;
|
|
14
|
+
disabled: boolean;
|
|
15
|
+
status: "new" | "existing" | "copy";
|
|
16
|
+
pluginActions: React.ReactNode[];
|
|
17
|
+
openEntityMode: "side_panel" | "full_screen";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function EntityFormActions({
|
|
21
|
+
collection,
|
|
22
|
+
entity,
|
|
23
|
+
layout,
|
|
24
|
+
savingError,
|
|
25
|
+
formex,
|
|
26
|
+
disabled,
|
|
27
|
+
status,
|
|
28
|
+
pluginActions,
|
|
29
|
+
openEntityMode
|
|
30
|
+
}: EntityFormActionsProps) {
|
|
31
|
+
|
|
32
|
+
const context = useFireCMSContext();
|
|
33
|
+
const sideEntityController = useSideEntityController();
|
|
34
|
+
|
|
35
|
+
return layout === "bottom"
|
|
36
|
+
? buildBottomActions({
|
|
37
|
+
savingError,
|
|
38
|
+
entity,
|
|
39
|
+
collection,
|
|
40
|
+
context,
|
|
41
|
+
sideEntityController,
|
|
42
|
+
isSubmitting: formex.isSubmitting,
|
|
43
|
+
disabled,
|
|
44
|
+
status,
|
|
45
|
+
pluginActions,
|
|
46
|
+
openEntityMode
|
|
47
|
+
})
|
|
48
|
+
: buildSideActions({
|
|
49
|
+
savingError,
|
|
50
|
+
entity,
|
|
51
|
+
collection,
|
|
52
|
+
context,
|
|
53
|
+
sideEntityController,
|
|
54
|
+
isSubmitting: formex.isSubmitting,
|
|
55
|
+
disabled,
|
|
56
|
+
status,
|
|
57
|
+
pluginActions,
|
|
58
|
+
openEntityMode
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type ActionsViewProps<M extends object> = {
|
|
63
|
+
savingError: Error | undefined,
|
|
64
|
+
entity: Entity<M> | undefined,
|
|
65
|
+
formActions?: EntityAction[],
|
|
66
|
+
collection: ResolvedEntityCollection,
|
|
67
|
+
context: FireCMSContext,
|
|
68
|
+
sideEntityController: SideEntityController,
|
|
69
|
+
isSubmitting: boolean,
|
|
70
|
+
disabled: boolean,
|
|
71
|
+
status: "new" | "existing" | "copy",
|
|
72
|
+
pluginActions?: React.ReactNode[],
|
|
73
|
+
openEntityMode: "side_panel" | "full_screen";
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function buildBottomActions<M extends object>({
|
|
77
|
+
savingError,
|
|
78
|
+
entity,
|
|
79
|
+
formActions,
|
|
80
|
+
collection,
|
|
81
|
+
context,
|
|
82
|
+
sideEntityController,
|
|
83
|
+
isSubmitting,
|
|
84
|
+
disabled,
|
|
85
|
+
status,
|
|
86
|
+
pluginActions,
|
|
87
|
+
openEntityMode
|
|
88
|
+
}: ActionsViewProps<M>) {
|
|
89
|
+
|
|
90
|
+
return <DialogActions position={"absolute"}>
|
|
91
|
+
{savingError &&
|
|
92
|
+
<div className="text-right">
|
|
93
|
+
<Typography color={"error"}>{savingError.message}</Typography>
|
|
94
|
+
</div>
|
|
95
|
+
}
|
|
96
|
+
{entity && (formActions ?? []).length > 0 && <div className="flex-grow flex overflow-auto no-scrollbar">
|
|
97
|
+
{(formActions ?? []).map(action => (
|
|
98
|
+
<IconButton
|
|
99
|
+
key={action.name}
|
|
100
|
+
color="primary"
|
|
101
|
+
onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
|
102
|
+
event.stopPropagation();
|
|
103
|
+
if (entity)
|
|
104
|
+
action.onClick({
|
|
105
|
+
entity,
|
|
106
|
+
fullPath: collection.path,
|
|
107
|
+
collection: collection,
|
|
108
|
+
context,
|
|
109
|
+
sideEntityController,
|
|
110
|
+
openEntityMode: openEntityMode
|
|
111
|
+
});
|
|
112
|
+
}}>
|
|
113
|
+
{action.icon}
|
|
114
|
+
</IconButton>
|
|
115
|
+
))}
|
|
116
|
+
</div>}
|
|
117
|
+
{pluginActions}
|
|
118
|
+
<Button variant="text" disabled={disabled || isSubmitting} type="reset">
|
|
119
|
+
{status === "existing" ? "Discard" : "Clear"}
|
|
120
|
+
</Button>
|
|
121
|
+
<Button variant={"filled"} color="primary" type="submit"
|
|
122
|
+
disabled={disabled || isSubmitting}>
|
|
123
|
+
{status === "existing" && "Save"}
|
|
124
|
+
{status === "copy" && "Create copy"}
|
|
125
|
+
{status === "new" && "Create"}
|
|
126
|
+
</Button>
|
|
127
|
+
|
|
128
|
+
</DialogActions>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function buildSideActions<M extends object>({
|
|
132
|
+
savingError,
|
|
133
|
+
entity,
|
|
134
|
+
formActions,
|
|
135
|
+
collection,
|
|
136
|
+
context,
|
|
137
|
+
sideEntityController,
|
|
138
|
+
isSubmitting,
|
|
139
|
+
disabled,
|
|
140
|
+
status,
|
|
141
|
+
pluginActions
|
|
142
|
+
}: ActionsViewProps<M>) {
|
|
143
|
+
|
|
144
|
+
return <div
|
|
145
|
+
className={cls("overflow-auto h-full flex flex-col gap-2 w-80 2xl:w-96 px-4 py-16 sticky top-0 border-l", defaultBorderMixin)}>
|
|
146
|
+
<LoadingButton fullWidth={true}
|
|
147
|
+
variant="filled"
|
|
148
|
+
color="primary"
|
|
149
|
+
type="submit"
|
|
150
|
+
size={"large"}
|
|
151
|
+
disabled={disabled || isSubmitting}>
|
|
152
|
+
{status === "existing" && "Save"}
|
|
153
|
+
{status === "copy" && "Create copy"}
|
|
154
|
+
{status === "new" && "Create"}
|
|
155
|
+
</LoadingButton>
|
|
156
|
+
<Button fullWidth={true} variant="text" disabled={disabled || isSubmitting} type="reset">
|
|
157
|
+
{status === "existing" ? "Discard" : "Clear"}
|
|
158
|
+
</Button>
|
|
159
|
+
|
|
160
|
+
{pluginActions}
|
|
161
|
+
|
|
162
|
+
{savingError &&
|
|
163
|
+
<div className="text-right">
|
|
164
|
+
<Typography color={"error"}>{savingError.message}</Typography>
|
|
165
|
+
</div>
|
|
166
|
+
}
|
|
167
|
+
</div>;
|
|
168
|
+
}
|