@firecms/collection_editor 3.0.0-beta.2-pre.2 → 3.0.0-beta.2-pre.3
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/form/Field.d.ts +53 -0
- package/dist/form/Formex.d.ts +4 -0
- package/dist/form/index.d.ts +5 -0
- package/dist/form/types.d.ts +25 -0
- package/dist/form/useCreateFormex.d.ts +9 -0
- package/dist/form/utils.d.ts +44 -0
- package/dist/index.es.js +2612 -2328
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +3 -2
- package/dist/types/config_controller.d.ts +3 -3
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +3 -5
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +2 -2
- package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -2
- package/dist/ui/collection_editor/EnumForm.d.ts +1 -2
- package/dist/ui/collection_editor/PropertyEditView.d.ts +5 -5
- package/dist/ui/collection_editor/PropertyTree.d.ts +14 -13
- package/dist/ui/collection_editor/SwitchControl.d.ts +8 -0
- package/dist/ui/collection_editor/properties/CommonPropertyFields.d.ts +0 -1
- package/dist/ui/collection_editor/util.d.ts +1 -0
- package/package.json +5 -5
- package/src/ConfigControllerProvider.tsx +23 -21
- package/src/form/Field.tsx +162 -0
- package/src/form/Formex.tsx +8 -0
- package/src/form/README.md +165 -0
- package/src/form/index.ts +5 -0
- package/src/form/types.ts +27 -0
- package/src/form/useCreateFormex.tsx +137 -0
- package/src/form/utils.ts +169 -0
- package/src/types/collection_editor_controller.tsx +4 -3
- package/src/types/config_controller.tsx +3 -3
- package/src/ui/CollectionViewHeaderAction.tsx +1 -1
- package/src/ui/EditorCollectionAction.tsx +3 -3
- package/src/ui/HomePageEditorCollectionAction.tsx +2 -2
- package/src/ui/MissingReferenceWidget.tsx +2 -1
- package/src/ui/NewCollectionButton.tsx +3 -3
- package/src/ui/NewCollectionCard.tsx +2 -1
- package/src/ui/PropertyAddColumnComponent.tsx +1 -1
- package/src/ui/RootCollectionSuggestions.tsx +2 -1
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +2 -2
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +422 -374
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +19 -12
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +26 -18
- package/src/ui/collection_editor/EnumForm.tsx +118 -114
- package/src/ui/collection_editor/GetCodeDialog.tsx +1 -1
- package/src/ui/collection_editor/PropertyEditView.tsx +198 -142
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +5 -1
- package/src/ui/collection_editor/PropertyTree.tsx +132 -113
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +18 -11
- package/src/ui/collection_editor/SwitchControl.tsx +39 -0
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +10 -2
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +13 -9
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +11 -37
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -6
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +11 -14
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +10 -9
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +15 -9
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +27 -18
- package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +2 -2
- package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +27 -16
- package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +33 -18
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +99 -80
- package/src/ui/collection_editor/util.ts +7 -0
- package/src/ui/collection_editor/utils/strings.ts +2 -1
|
@@ -1,53 +1,60 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
2
|
import { EntityCollection, unslugify, } from "@firecms/core";
|
|
3
3
|
import { Button, Card, Chip, CircularProgress, cn, Container, Icon, Tooltip, Typography, } from "@firecms/ui";
|
|
4
|
-
import { useFormikContext } from "formik";
|
|
5
4
|
|
|
6
5
|
import { productsCollectionTemplate } from "./templates/products_template";
|
|
7
6
|
import { blogCollectionTemplate } from "./templates/blog_template";
|
|
8
7
|
import { usersCollectionTemplate } from "./templates/users_template";
|
|
9
8
|
import { ImportFileUpload } from "@firecms/data_import_export";
|
|
10
9
|
import { pagesCollectionTemplate } from "./templates/pages_template";
|
|
10
|
+
import { useFormex } from "../../form";
|
|
11
11
|
|
|
12
12
|
export function CollectionEditorWelcomeView({
|
|
13
13
|
path,
|
|
14
14
|
pathSuggestions,
|
|
15
15
|
parentCollection,
|
|
16
16
|
onContinue,
|
|
17
|
-
|
|
17
|
+
existingCollectionPaths
|
|
18
18
|
}: {
|
|
19
19
|
path: string;
|
|
20
20
|
pathSuggestions?: (path: string) => Promise<string[]>;
|
|
21
21
|
parentCollection?: EntityCollection;
|
|
22
22
|
onContinue: (importData?: object[]) => void;
|
|
23
|
-
|
|
23
|
+
existingCollectionPaths?: string[];
|
|
24
24
|
}) {
|
|
25
25
|
|
|
26
26
|
const [loadingPathSuggestions, setLoadingPathSuggestions] = useState(false);
|
|
27
27
|
const [filteredPathSuggestions, setFilteredPathSuggestions] = useState<string[] | undefined>();
|
|
28
28
|
useEffect(() => {
|
|
29
|
-
if (pathSuggestions &&
|
|
29
|
+
if (pathSuggestions && existingCollectionPaths) {
|
|
30
30
|
setLoadingPathSuggestions(true);
|
|
31
31
|
pathSuggestions(path)
|
|
32
32
|
.then(suggestions => {
|
|
33
|
-
const filteredSuggestions = suggestions.filter(s => !
|
|
33
|
+
const filteredSuggestions = suggestions.filter(s => !(existingCollectionPaths ?? []).find(c => c.trim().toLowerCase() === s.trim().toLowerCase()));
|
|
34
34
|
setFilteredPathSuggestions(filteredSuggestions);
|
|
35
35
|
})
|
|
36
36
|
.finally(() => setLoadingPathSuggestions(false));
|
|
37
37
|
}
|
|
38
|
-
}, [
|
|
38
|
+
}, [existingCollectionPaths, path, pathSuggestions]);
|
|
39
|
+
|
|
40
|
+
// const {
|
|
41
|
+
// values,
|
|
42
|
+
// setFieldValue,
|
|
43
|
+
// setValues,
|
|
44
|
+
// handleChange,
|
|
45
|
+
// touched,
|
|
46
|
+
// errors,
|
|
47
|
+
// setFieldTouched,
|
|
48
|
+
// isSubmitting,
|
|
49
|
+
// submitCount
|
|
50
|
+
// } = useFormex<EntityCollection>();
|
|
39
51
|
|
|
40
52
|
const {
|
|
41
53
|
values,
|
|
42
54
|
setFieldValue,
|
|
43
55
|
setValues,
|
|
44
|
-
handleChange,
|
|
45
|
-
touched,
|
|
46
|
-
errors,
|
|
47
|
-
setFieldTouched,
|
|
48
|
-
isSubmitting,
|
|
49
56
|
submitCount
|
|
50
|
-
} =
|
|
57
|
+
} = useFormex<EntityCollection>();
|
|
51
58
|
|
|
52
59
|
return (
|
|
53
60
|
<div className={"overflow-auto my-auto"}>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import { Field,
|
|
3
|
+
import { Field, getIn } from "../../form";
|
|
4
4
|
import {
|
|
5
5
|
EntityCollection,
|
|
6
6
|
ErrorBoundary,
|
|
@@ -34,12 +34,13 @@ import { OnPropertyChangedParams, PropertyForm, PropertyFormDialog } from "./Pro
|
|
|
34
34
|
import { PropertyTree } from "./PropertyTree";
|
|
35
35
|
import { PersistedCollection } from "../../types/persisted_collection";
|
|
36
36
|
import { GetCodeDialog } from "./GetCodeDialog";
|
|
37
|
+
import { useFormex } from "../../form/Formex";
|
|
37
38
|
|
|
38
39
|
type CollectionEditorFormProps = {
|
|
39
40
|
showErrors: boolean;
|
|
40
41
|
isNewCollection: boolean;
|
|
41
42
|
propertyErrorsRef?: React.MutableRefObject<any>;
|
|
42
|
-
onPropertyError: (propertyKey: string, namespace: string | undefined, error?:
|
|
43
|
+
onPropertyError: (propertyKey: string, namespace: string | undefined, error?: Record<string, any>) => void;
|
|
43
44
|
setDirty?: (dirty: boolean) => void;
|
|
44
45
|
reservedGroups?: string[];
|
|
45
46
|
extraIcon: React.ReactNode;
|
|
@@ -72,7 +73,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
72
73
|
setFieldTouched,
|
|
73
74
|
errors,
|
|
74
75
|
dirty
|
|
75
|
-
} =
|
|
76
|
+
} = useFormex<PersistedCollection>();
|
|
76
77
|
|
|
77
78
|
const snackbarController = useSnackbarController();
|
|
78
79
|
|
|
@@ -156,20 +157,20 @@ export function CollectionPropertiesEditorForm({
|
|
|
156
157
|
}
|
|
157
158
|
: undefined;
|
|
158
159
|
|
|
159
|
-
const getCurrentPropertiesOrder = (namespace?: string) => {
|
|
160
|
+
const getCurrentPropertiesOrder = useCallback((namespace?: string) => {
|
|
160
161
|
if (!namespace) return currentPropertiesOrderRef.current[""];
|
|
161
162
|
return currentPropertiesOrderRef.current[namespace] ?? getIn(values, namespaceToPropertiesOrderPath(namespace));
|
|
162
|
-
}
|
|
163
|
+
}, [values]);
|
|
163
164
|
|
|
164
|
-
const updatePropertiesOrder = (newPropertiesOrder: string[], namespace?: string) => {
|
|
165
|
+
const updatePropertiesOrder = useCallback((newPropertiesOrder: string[], namespace?: string) => {
|
|
165
166
|
const propertiesOrderPath = namespaceToPropertiesOrderPath(namespace);
|
|
166
167
|
|
|
167
168
|
setFieldValue(propertiesOrderPath, newPropertiesOrder, false);
|
|
168
169
|
currentPropertiesOrderRef.current[namespace ?? ""] = newPropertiesOrder;
|
|
169
170
|
|
|
170
|
-
};
|
|
171
|
+
}, [setFieldValue]);
|
|
171
172
|
|
|
172
|
-
const deleteProperty = (propertyKey?: string, namespace?: string) => {
|
|
173
|
+
const deleteProperty = useCallback((propertyKey?: string, namespace?: string) => {
|
|
173
174
|
const fullId = propertyKey ? getFullId(propertyKey, namespace) : undefined;
|
|
174
175
|
if (!fullId)
|
|
175
176
|
throw Error("collection editor miss config");
|
|
@@ -185,7 +186,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
185
186
|
setSelectedPropertyIndex(undefined);
|
|
186
187
|
setSelectedPropertyKey(undefined);
|
|
187
188
|
setSelectedPropertyNamespace(undefined);
|
|
188
|
-
};
|
|
189
|
+
}, [getCurrentPropertiesOrder, setFieldValue, updatePropertiesOrder]);
|
|
189
190
|
|
|
190
191
|
const onPropertyMove = (propertiesOrder: string[], namespace?: string) => {
|
|
191
192
|
setFieldValue(namespaceToPropertiesOrderPath(namespace), propertiesOrder, false);
|
|
@@ -263,9 +264,9 @@ export function CollectionPropertiesEditorForm({
|
|
|
263
264
|
|
|
264
265
|
};
|
|
265
266
|
|
|
266
|
-
const onPropertyErrorInternal = useCallback((id: string, namespace?: string, error?:
|
|
267
|
+
const onPropertyErrorInternal = useCallback((id: string, namespace?: string, error?: Record<string, any>) => {
|
|
267
268
|
const propertyPath = id ? getFullId(id, namespace) : undefined;
|
|
268
|
-
console.
|
|
269
|
+
console.debug("onPropertyErrorInternal", {
|
|
269
270
|
id,
|
|
270
271
|
namespace,
|
|
271
272
|
error,
|
|
@@ -292,6 +293,17 @@ export function CollectionPropertiesEditorForm({
|
|
|
292
293
|
: Object.keys(values.properties)) as string[];
|
|
293
294
|
|
|
294
295
|
const owner = useMemo(() => getUser(values.ownerId), [getUser, values.ownerId]);
|
|
296
|
+
|
|
297
|
+
const onPropertyClick = useCallback((propertyKey: string, namespace?: string) => {
|
|
298
|
+
console.debug("CollectionEditor: onPropertyClick", {
|
|
299
|
+
propertyKey,
|
|
300
|
+
namespace
|
|
301
|
+
});
|
|
302
|
+
setSelectedPropertyIndex(usedPropertiesOrder.indexOf(propertyKey));
|
|
303
|
+
setSelectedPropertyKey(propertyKey);
|
|
304
|
+
setSelectedPropertyNamespace(namespace);
|
|
305
|
+
}, [usedPropertiesOrder]);
|
|
306
|
+
|
|
295
307
|
const body = (
|
|
296
308
|
<div className={"grid grid-cols-12 gap-2 h-full bg-gray-50 dark:bg-gray-900"}>
|
|
297
309
|
<div className={cn(
|
|
@@ -357,20 +369,16 @@ export function CollectionPropertiesEditorForm({
|
|
|
357
369
|
<ErrorBoundary>
|
|
358
370
|
<PropertyTree
|
|
359
371
|
className={"pl-8"}
|
|
360
|
-
onPropertyClick={(propertyKey, namespace) => {
|
|
361
|
-
setSelectedPropertyIndex(usedPropertiesOrder.indexOf(propertyKey));
|
|
362
|
-
setSelectedPropertyKey(propertyKey);
|
|
363
|
-
setSelectedPropertyNamespace(namespace);
|
|
364
|
-
}}
|
|
365
372
|
inferredPropertyKeys={inferredPropertyKeys}
|
|
366
373
|
selectedPropertyKey={selectedPropertyKey ? getFullId(selectedPropertyKey, selectedPropertyNamespace) : undefined}
|
|
367
374
|
properties={values.properties}
|
|
368
375
|
additionalFields={values.additionalFields}
|
|
369
376
|
propertiesOrder={usedPropertiesOrder}
|
|
377
|
+
onPropertyClick={onPropertyClick}
|
|
370
378
|
onPropertyMove={onPropertyMove}
|
|
371
379
|
onPropertyRemove={isNewCollection ? deleteProperty : undefined}
|
|
372
380
|
collectionEditable={collectionEditable}
|
|
373
|
-
errors={
|
|
381
|
+
errors={errors}/>
|
|
374
382
|
</ErrorBoundary>
|
|
375
383
|
|
|
376
384
|
<Button className={"mt-8 w-full"}
|
|
@@ -384,7 +392,7 @@ export function CollectionPropertiesEditorForm({
|
|
|
384
392
|
</div>
|
|
385
393
|
|
|
386
394
|
{!asDialog &&
|
|
387
|
-
<div className={"col-span-12 lg:col-span-7
|
|
395
|
+
<div className={"col-span-12 lg:col-span-7 p-4 md:py-8 md:px-4 h-full overflow-auto pb-20 md:pb-20"}>
|
|
388
396
|
<Paper
|
|
389
397
|
className="sticky top-8 p-4 min-h-full border border-transparent w-full flex flex-col justify-center ">
|
|
390
398
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React, { useEffect } from "react";
|
|
2
|
+
import equal from "react-fast-compare"
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import { EnumValueConfig, EnumValues, FormikArrayContainer, } from "@firecms/core";
|
|
4
|
+
import { ArrayContainer, EnumValueConfig, EnumValues, } from "@firecms/core";
|
|
5
5
|
import {
|
|
6
6
|
AutoAwesomeIcon,
|
|
7
|
+
Badge,
|
|
7
8
|
Button,
|
|
8
9
|
CircularProgress,
|
|
9
10
|
DebouncedTextField,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
} from "@firecms/ui";
|
|
19
20
|
import { FieldHelperView } from "./properties/FieldHelperView";
|
|
20
21
|
import { extractEnumFromValues } from "@firecms/schema_inference";
|
|
22
|
+
import { Field, Formex, getIn, useCreateFormex, useFormex } from "../../form";
|
|
21
23
|
|
|
22
24
|
type EnumFormProps = {
|
|
23
25
|
enumValues: EnumValueConfig[];
|
|
@@ -28,64 +30,68 @@ type EnumFormProps = {
|
|
|
28
30
|
allowDataInference?: boolean;
|
|
29
31
|
getData?: () => Promise<string[]>;
|
|
30
32
|
};
|
|
31
|
-
export const EnumForm = React.memo(
|
|
32
|
-
function EnumForm({
|
|
33
|
-
enumValues,
|
|
34
|
-
onValuesChanged,
|
|
35
|
-
onError,
|
|
36
|
-
updateIds,
|
|
37
|
-
disabled,
|
|
38
|
-
allowDataInference,
|
|
39
|
-
getData
|
|
40
|
-
}: EnumFormProps) {
|
|
41
33
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
disabled={disabled}
|
|
71
|
-
allowDataInference={allowDataInference}
|
|
72
|
-
getData={getData}/>
|
|
34
|
+
export function EnumForm({
|
|
35
|
+
enumValues,
|
|
36
|
+
onValuesChanged,
|
|
37
|
+
onError,
|
|
38
|
+
updateIds,
|
|
39
|
+
disabled,
|
|
40
|
+
allowDataInference,
|
|
41
|
+
getData
|
|
42
|
+
}: EnumFormProps) {
|
|
43
|
+
|
|
44
|
+
const formex = useCreateFormex<{
|
|
45
|
+
enumValues: EnumValueConfig[]
|
|
46
|
+
}>({
|
|
47
|
+
initialValues: { enumValues },
|
|
48
|
+
validateOnChange: true,
|
|
49
|
+
validation: (values) => {
|
|
50
|
+
const errors: any = {};
|
|
51
|
+
if (values.enumValues) {
|
|
52
|
+
values.enumValues.forEach((enumValue, index) => {
|
|
53
|
+
if (!enumValue?.label) {
|
|
54
|
+
errors.enumValues = errors.enumValues ?? [];
|
|
55
|
+
errors.enumValues[index] = errors.enumValues[index] ?? {};
|
|
56
|
+
errors.enumValues[index].label = "You must specify a label for this enum value entry";
|
|
57
|
+
}
|
|
58
|
+
if (!enumValue?.id) {
|
|
59
|
+
errors.enumValues = errors.enumValues ?? [];
|
|
60
|
+
errors.enumValues[index] = errors.enumValues[index] ?? {};
|
|
61
|
+
errors.enumValues[index].id = "You must specify an ID for this enum value entry";
|
|
73
62
|
}
|
|
74
|
-
}
|
|
75
|
-
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const hasError = Boolean(errors?.enumValues && Object.keys(errors?.enumValues).length > 0);
|
|
66
|
+
onError?.(hasError);
|
|
67
|
+
return errors;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const { values, errors } = formex;
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (onValuesChanged) {
|
|
75
|
+
onValuesChanged(values.enumValues);
|
|
76
|
+
}
|
|
77
|
+
}, [values.enumValues]);
|
|
78
|
+
|
|
79
|
+
return <Formex value={formex}>
|
|
80
|
+
<EnumFormFields enumValuesPath={"enumValues"}
|
|
81
|
+
values={values}
|
|
82
|
+
errors={errors}
|
|
83
|
+
shouldUpdateId={updateIds}
|
|
84
|
+
disabled={disabled}
|
|
85
|
+
allowDataInference={allowDataInference}
|
|
86
|
+
getData={getData}/>
|
|
87
|
+
</Formex>
|
|
76
88
|
|
|
77
|
-
|
|
78
|
-
},
|
|
79
|
-
function areEqual(prevProps: EnumFormProps, nextProps: EnumFormProps) {
|
|
80
|
-
return prevProps.enumValues.length === nextProps.enumValues.length &&
|
|
81
|
-
prevProps.onValuesChanged === nextProps.onValuesChanged &&
|
|
82
|
-
prevProps.getData === nextProps.getData
|
|
83
|
-
;
|
|
84
|
-
}
|
|
85
|
-
);
|
|
89
|
+
}
|
|
86
90
|
|
|
87
91
|
type EnumFormFieldsProps = {
|
|
88
|
-
values: {
|
|
92
|
+
values: {
|
|
93
|
+
enumValues: EnumValueConfig[]
|
|
94
|
+
};
|
|
89
95
|
errors: any;
|
|
90
96
|
enumValuesPath: string;
|
|
91
97
|
shouldUpdateId: boolean;
|
|
@@ -107,7 +113,7 @@ function EnumFormFields({
|
|
|
107
113
|
|
|
108
114
|
const {
|
|
109
115
|
setFieldValue
|
|
110
|
-
} =
|
|
116
|
+
} = useFormex();
|
|
111
117
|
|
|
112
118
|
const [lastInternalIdAdded, setLastInternalIdAdded] = React.useState<number | undefined>();
|
|
113
119
|
const [editDialogIndex, setEditDialogIndex] = React.useState<number | undefined>();
|
|
@@ -118,10 +124,12 @@ function EnumFormFields({
|
|
|
118
124
|
|
|
119
125
|
const buildEntry = (index: number, internalId: number) => {
|
|
120
126
|
const justAdded = lastInternalIdAdded === internalId;
|
|
127
|
+
const entryError = errors?.enumValues && errors?.enumValues[index];
|
|
121
128
|
return <EnumEntry index={index}
|
|
122
129
|
disabled={disabled}
|
|
123
130
|
enumValuesPath={enumValuesPath}
|
|
124
131
|
autoFocus={justAdded}
|
|
132
|
+
entryError={entryError}
|
|
125
133
|
shouldUpdateId={shouldUpdateId || justAdded}
|
|
126
134
|
onDialogOpen={() => setEditDialogIndex(index)}
|
|
127
135
|
inferredEntry={inferredValues.has(values.enumValues[index]?.id as string)}
|
|
@@ -151,7 +159,7 @@ function EnumFormFields({
|
|
|
151
159
|
newEnumValues.forEach((enumValue) => {
|
|
152
160
|
inferredValues.add(enumValue.id);
|
|
153
161
|
});
|
|
154
|
-
setFieldValue(enumValuesPath, [...newEnumValues, ...currentEnumValues]);
|
|
162
|
+
setFieldValue(enumValuesPath, [...newEnumValues, ...currentEnumValues], true);
|
|
155
163
|
}).catch(e => {
|
|
156
164
|
console.error(e);
|
|
157
165
|
})
|
|
@@ -179,16 +187,16 @@ function EnumFormFields({
|
|
|
179
187
|
|
|
180
188
|
<Paper className="p-4 m-1">
|
|
181
189
|
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
<ArrayContainer droppableId={enumValuesPath}
|
|
191
|
+
addLabel={"Add enum value"}
|
|
192
|
+
value={values.enumValues}
|
|
193
|
+
disabled={disabled}
|
|
194
|
+
size={"small"}
|
|
195
|
+
buildEntry={buildEntry}
|
|
196
|
+
onInternalIdAdded={setLastInternalIdAdded}
|
|
197
|
+
includeAddButton={true}
|
|
198
|
+
onValueChange={(value) => setFieldValue(enumValuesPath, value)}
|
|
199
|
+
newDefaultEntry={{ id: "", label: "" }}/>
|
|
192
200
|
|
|
193
201
|
<EnumEntryDialog index={editDialogIndex}
|
|
194
202
|
open={editDialogIndex !== undefined}
|
|
@@ -207,6 +215,7 @@ type EnumEntryProps = {
|
|
|
207
215
|
onDialogOpen: () => void;
|
|
208
216
|
disabled: boolean;
|
|
209
217
|
inferredEntry?: boolean;
|
|
218
|
+
entryError?: { label?: string, id?: string }
|
|
210
219
|
};
|
|
211
220
|
|
|
212
221
|
const EnumEntry = React.memo(
|
|
@@ -217,7 +226,8 @@ const EnumEntry = React.memo(
|
|
|
217
226
|
autoFocus,
|
|
218
227
|
onDialogOpen,
|
|
219
228
|
disabled,
|
|
220
|
-
inferredEntry
|
|
229
|
+
inferredEntry,
|
|
230
|
+
entryError
|
|
221
231
|
}: EnumEntryProps) {
|
|
222
232
|
|
|
223
233
|
const {
|
|
@@ -226,7 +236,7 @@ const EnumEntry = React.memo(
|
|
|
226
236
|
errors,
|
|
227
237
|
setFieldValue,
|
|
228
238
|
touched
|
|
229
|
-
} =
|
|
239
|
+
} = useFormex<EnumValues>();
|
|
230
240
|
|
|
231
241
|
const shouldUpdateIdRef = React.useRef(!getIn(values, `${enumValuesPath}[${index}].id`));
|
|
232
242
|
const shouldUpdateId = updateId || shouldUpdateIdRef.current;
|
|
@@ -234,8 +244,6 @@ const EnumEntry = React.memo(
|
|
|
234
244
|
const idValue = getIn(values, `${enumValuesPath}[${index}].id`);
|
|
235
245
|
const labelValue = getIn(values, `${enumValuesPath}[${index}].label`);
|
|
236
246
|
|
|
237
|
-
const labelError = getIn(errors, `${enumValuesPath}[${index}].label`);
|
|
238
|
-
|
|
239
247
|
const currentLabelRef = React.useRef(labelValue);
|
|
240
248
|
|
|
241
249
|
React.useEffect(() => {
|
|
@@ -246,35 +254,50 @@ const EnumEntry = React.memo(
|
|
|
246
254
|
}, [labelValue]);
|
|
247
255
|
|
|
248
256
|
return (
|
|
249
|
-
|
|
250
|
-
<
|
|
257
|
+
<>
|
|
258
|
+
<div className={"flex w-full align-center justify-center"}>
|
|
259
|
+
<Field name={`${enumValuesPath}[${index}].label`}
|
|
251
260
|
as={DebouncedTextField}
|
|
252
261
|
className={"flex-grow"}
|
|
253
262
|
required
|
|
254
263
|
disabled={disabled}
|
|
255
264
|
size="small"
|
|
256
|
-
validate={validateLabel}
|
|
257
265
|
autoFocus={autoFocus}
|
|
258
266
|
autoComplete="off"
|
|
259
267
|
endAdornment={inferredEntry && <AutoAwesomeIcon size={"small"}/>}
|
|
260
|
-
error={Boolean(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
error={Boolean(entryError?.label)}/>
|
|
269
|
+
|
|
270
|
+
{!disabled &&
|
|
271
|
+
<Badge color={"error"} invisible={!entryError?.id}>
|
|
272
|
+
<IconButton
|
|
273
|
+
size="small"
|
|
274
|
+
aria-label="edit"
|
|
275
|
+
className={"m-1"}
|
|
276
|
+
onClick={() => onDialogOpen()}>
|
|
277
|
+
<SettingsIcon size={"small"}/>
|
|
278
|
+
</IconButton>
|
|
279
|
+
</Badge>}
|
|
280
|
+
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
{entryError?.label && <Typography variant={"caption"}
|
|
284
|
+
className={"ml-3.5 text-red-500 dark:text-red-500"}>
|
|
285
|
+
{entryError?.label}
|
|
286
|
+
</Typography>}
|
|
287
|
+
|
|
288
|
+
{entryError?.id && <Typography variant={"caption"}
|
|
289
|
+
className={"ml-3.5 text-red-500 dark:text-red-500"}>
|
|
290
|
+
{entryError?.id}
|
|
291
|
+
</Typography>}
|
|
292
|
+
|
|
293
|
+
</>);
|
|
272
294
|
},
|
|
273
295
|
function areEqual(prevProps: EnumEntryProps, nextProps: EnumEntryProps) {
|
|
274
296
|
return prevProps.index === nextProps.index &&
|
|
275
297
|
prevProps.enumValuesPath === nextProps.enumValuesPath &&
|
|
276
298
|
prevProps.shouldUpdateId === nextProps.shouldUpdateId &&
|
|
277
299
|
prevProps.inferredEntry === nextProps.inferredEntry &&
|
|
300
|
+
equal(prevProps.entryError, nextProps.entryError) &&
|
|
278
301
|
prevProps.autoFocus === nextProps.autoFocus;
|
|
279
302
|
}
|
|
280
303
|
);
|
|
@@ -292,12 +315,8 @@ function EnumEntryDialog({
|
|
|
292
315
|
}) {
|
|
293
316
|
|
|
294
317
|
const {
|
|
295
|
-
values,
|
|
296
|
-
handleChange,
|
|
297
318
|
errors,
|
|
298
|
-
|
|
299
|
-
touched
|
|
300
|
-
} = useFormikContext<EnumValues>();
|
|
319
|
+
} = useFormex<EnumValues>();
|
|
301
320
|
|
|
302
321
|
const idError = index !== undefined ? getIn(errors, `${enumValuesPath}[${index}].id`) : undefined;
|
|
303
322
|
return <Dialog
|
|
@@ -309,14 +328,14 @@ function EnumEntryDialog({
|
|
|
309
328
|
|
|
310
329
|
<DialogContent>
|
|
311
330
|
{index !== undefined &&
|
|
312
|
-
<div
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
331
|
+
<div>
|
|
332
|
+
<Field name={`${enumValuesPath}[${index}].id`}
|
|
333
|
+
as={DebouncedTextField}
|
|
334
|
+
required
|
|
335
|
+
label={"ID"}
|
|
336
|
+
size="small"
|
|
337
|
+
autoComplete="off"
|
|
338
|
+
error={Boolean(idError)}/>
|
|
320
339
|
|
|
321
340
|
<FieldHelperView error={Boolean(idError)}>
|
|
322
341
|
{idError ?? "Value saved in the data source"}
|
|
@@ -337,18 +356,3 @@ function EnumEntryDialog({
|
|
|
337
356
|
</Dialog>
|
|
338
357
|
}
|
|
339
358
|
|
|
340
|
-
function validateLabel(value: string) {
|
|
341
|
-
let error;
|
|
342
|
-
if (!value) {
|
|
343
|
-
error = "You must specify a label";
|
|
344
|
-
}
|
|
345
|
-
return error;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function validateId(value: string) {
|
|
349
|
-
let error;
|
|
350
|
-
if (!value) {
|
|
351
|
-
error = "You must specify an ID";
|
|
352
|
-
}
|
|
353
|
-
return error;
|
|
354
|
-
}
|
|
@@ -9,7 +9,7 @@ export function GetCodeDialog({ collection, onOpenChange, open }: { onOpenChange
|
|
|
9
9
|
|
|
10
10
|
const snackbarController = useSnackbarController();
|
|
11
11
|
|
|
12
|
-
const code = "import { EntityCollection } from \"firecms\";\n\nconst " + camelCase(collection.name) + "Collection:EntityCollection = " + JSON5.stringify(collectionToCode(collection), null, "\t");
|
|
12
|
+
const code = "import { EntityCollection } from \"firecms\";\n\nconst " + (collection.name ? camelCase(collection.name) : "my") + "Collection:EntityCollection = " + JSON5.stringify(collectionToCode(collection), null, "\t");
|
|
13
13
|
return <Dialog open={open}
|
|
14
14
|
onOpenChange={onOpenChange}
|
|
15
15
|
maxWidth={"4xl"}>
|