@firecms/collection_editor 3.0.0-alpha.9 → 3.0.0-beta.10
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/LICENSE +114 -21
- package/dist/ConfigControllerProvider.d.ts +13 -3
- package/dist/index.d.ts +4 -2
- package/dist/index.es.js +5587 -4976
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +6829 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +36 -8
- package/dist/types/collection_inference.d.ts +1 -1
- package/dist/types/config_controller.d.ts +33 -6
- package/dist/types/persisted_collection.d.ts +4 -2
- package/dist/ui/CollectionViewHeaderAction.d.ts +11 -0
- package/dist/{components → ui}/EditorCollectionAction.d.ts +1 -1
- package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
- package/dist/ui/MissingReferenceWidget.d.ts +3 -0
- package/dist/ui/NewCollectionButton.d.ts +1 -0
- package/dist/ui/PropertyAddColumnComponent.d.ts +8 -0
- package/dist/{components → ui}/collection_editor/CollectionDetailsForm.d.ts +3 -2
- package/dist/{components → ui}/collection_editor/CollectionEditorDialog.d.ts +15 -11
- package/dist/{components → ui}/collection_editor/CollectionEditorWelcomeView.d.ts +3 -3
- package/dist/{components → ui}/collection_editor/CollectionPropertiesEditorForm.d.ts +8 -6
- package/dist/{components → ui}/collection_editor/CollectionYupValidation.d.ts +3 -0
- package/dist/ui/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
- package/dist/{components → ui}/collection_editor/EnumForm.d.ts +1 -2
- package/dist/ui/collection_editor/GetCodeDialog.d.ts +5 -0
- package/dist/{components → ui}/collection_editor/PropertyEditView.d.ts +21 -11
- package/dist/{components → ui}/collection_editor/PropertyFieldPreview.d.ts +4 -3
- package/dist/{components → ui}/collection_editor/PropertyTree.d.ts +11 -7
- package/dist/{components → ui}/collection_editor/SubcollectionsEditTab.d.ts +3 -3
- package/dist/ui/collection_editor/SwitchControl.d.ts +8 -0
- package/dist/{components → ui}/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
- package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +14 -0
- package/dist/{components → ui}/collection_editor/import/clean_import_data.d.ts +1 -1
- package/dist/{components → ui}/collection_editor/properties/BlockPropertyField.d.ts +4 -1
- package/dist/{components → ui}/collection_editor/properties/CommonPropertyFields.d.ts +1 -1
- package/dist/{components → ui}/collection_editor/properties/MapPropertyField.d.ts +4 -1
- package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
- package/dist/{components → ui}/collection_editor/properties/RepeatPropertyField.d.ts +4 -1
- package/dist/{components → ui}/collection_editor/properties/StringPropertyField.d.ts +1 -1
- package/dist/ui/collection_editor/properties/UrlPropertyField.d.ts +4 -0
- package/dist/ui/collection_editor/templates/blog_template.d.ts +2 -0
- package/dist/ui/collection_editor/templates/pages_template.d.ts +2 -0
- package/dist/ui/collection_editor/templates/products_template.d.ts +2 -0
- package/dist/ui/collection_editor/templates/users_template.d.ts +2 -0
- package/dist/{components → ui}/collection_editor/util.d.ts +1 -0
- package/dist/ui/collection_editor/utils/strings.d.ts +1 -0
- package/dist/ui/collection_editor/utils/supported_fields.d.ts +3 -0
- package/dist/ui/collection_editor/utils/update_property_for_widget.d.ts +2 -0
- package/dist/useCollectionEditorPlugin.d.ts +17 -6
- package/dist/utils/collections.d.ts +6 -0
- package/dist/utils/entities.d.ts +3 -4
- package/package.json +35 -37
- package/src/ConfigControllerProvider.tsx +350 -0
- package/src/index.ts +36 -0
- package/src/types/collection_editor_controller.tsx +53 -0
- package/src/types/collection_inference.ts +3 -0
- package/src/types/config_controller.tsx +60 -0
- package/src/types/config_permissions.ts +20 -0
- package/src/types/persisted_collection.ts +9 -0
- package/src/ui/CollectionViewHeaderAction.tsx +48 -0
- package/src/ui/EditorCollectionAction.tsx +56 -0
- package/src/ui/EditorCollectionActionStart.tsx +88 -0
- package/src/ui/HomePageEditorCollectionAction.tsx +89 -0
- package/src/ui/MissingReferenceWidget.tsx +37 -0
- package/src/ui/NewCollectionButton.tsx +18 -0
- package/src/ui/NewCollectionCard.tsx +48 -0
- package/src/ui/PropertyAddColumnComponent.tsx +47 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +426 -0
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +826 -0
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +214 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +513 -0
- package/src/ui/collection_editor/CollectionYupValidation.tsx +7 -0
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +37 -0
- package/src/ui/collection_editor/EnumForm.tsx +357 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +122 -0
- package/src/ui/collection_editor/PropertyEditView.tsx +789 -0
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +204 -0
- package/src/ui/collection_editor/PropertyTree.tsx +254 -0
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +269 -0
- package/src/ui/collection_editor/SwitchControl.tsx +39 -0
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +47 -0
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +53 -0
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +299 -0
- package/src/ui/collection_editor/import/clean_import_data.ts +53 -0
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +144 -0
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +40 -0
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +110 -0
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +89 -0
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +114 -0
- package/src/ui/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +150 -0
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +38 -0
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +160 -0
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +108 -0
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +215 -0
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +70 -0
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +89 -0
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +45 -0
- package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
- package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +61 -0
- package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +115 -0
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +150 -0
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
- package/src/ui/collection_editor/templates/blog_template.ts +115 -0
- package/src/ui/collection_editor/templates/pages_template.ts +183 -0
- package/src/ui/collection_editor/templates/products_template.ts +88 -0
- package/src/ui/collection_editor/templates/users_template.ts +42 -0
- package/src/ui/collection_editor/util.ts +28 -0
- package/src/ui/collection_editor/utils/strings.ts +9 -0
- package/src/ui/collection_editor/utils/supported_fields.tsx +29 -0
- package/src/ui/collection_editor/utils/update_property_for_widget.ts +271 -0
- package/src/ui/collection_editor/utils/useTraceUpdate.tsx +23 -0
- package/src/useCollectionEditorController.tsx +9 -0
- package/src/useCollectionEditorPlugin.tsx +164 -0
- package/src/useCollectionsConfigController.tsx +9 -0
- package/src/utils/arrays.ts +3 -0
- package/src/utils/collections.ts +30 -0
- package/src/utils/entities.ts +38 -0
- package/src/vite-env.d.ts +1 -0
- package/dist/components/collection_editor/PropertySelectItem.d.ts +0 -8
- package/dist/components/collection_editor/SelectIcons.d.ts +0 -6
- package/dist/components/collection_editor/import/CollectionEditorImportMapping.d.ts +0 -4
- package/dist/components/collection_editor/properties/FieldHelperView.d.ts +0 -4
- package/dist/components/collection_editor/templates/blog_template.d.ts +0 -10
- package/dist/components/collection_editor/templates/products_template.d.ts +0 -12
- package/dist/components/collection_editor/templates/users_template.d.ts +0 -7
- package/dist/components/collection_editor/utils/supported_fields.d.ts +0 -3
- package/dist/components/collection_editor/utils/update_property_for_widget.d.ts +0 -3
- package/dist/types/editable_properties.d.ts +0 -10
- package/dist/utils/icons.d.ts +0 -2
- package/dist/utils/synonyms.d.ts +0 -1951
- /package/dist/{components → ui}/HomePageEditorCollectionAction.d.ts +0 -0
- /package/dist/{components → ui}/NewCollectionCard.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/UnsavedChangesDialog.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/BooleanPropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/DateTimePropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/EnumPropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/KeyValuePropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/NumberPropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/ReferencePropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/StoragePropertyField.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/advanced/AdvancedPropertyValidation.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/validation/ArrayPropertyValidation.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/validation/GeneralPropertyValidation.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/validation/NumberPropertyValidation.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/validation/StringPropertyValidation.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/properties/validation/ValidationPanel.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/utils/useTraceUpdate.d.ts +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { getIn, useFormex } from "@firecms/formex";
|
|
3
|
+
import { FieldCaption, NumberProperty, StringProperty } from "@firecms/core";
|
|
4
|
+
import { Select, SelectItem } from "@firecms/ui";
|
|
5
|
+
import { GeneralPropertyValidation } from "./validation/GeneralPropertyValidation";
|
|
6
|
+
import { ValidationPanel } from "./validation/ValidationPanel";
|
|
7
|
+
|
|
8
|
+
export function DateTimePropertyField({ disabled }: {
|
|
9
|
+
disabled: boolean;
|
|
10
|
+
}) {
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
values,
|
|
14
|
+
errors,
|
|
15
|
+
touched,
|
|
16
|
+
setFieldValue
|
|
17
|
+
} = useFormex<StringProperty | NumberProperty>();
|
|
18
|
+
|
|
19
|
+
const modePath = "mode";
|
|
20
|
+
const modeValue: string | undefined = getIn(values, modePath);
|
|
21
|
+
const modeError: string | undefined = getIn(touched, modePath) && getIn(errors, modePath);
|
|
22
|
+
|
|
23
|
+
const autoValuePath = "autoValue";
|
|
24
|
+
const autoValueValue: string | undefined = getIn(values, autoValuePath);
|
|
25
|
+
const autoValueError: string | undefined = getIn(touched, autoValuePath) && getIn(errors, autoValuePath);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
<div className={"flex flex-col col-span-12 gap-2"}>
|
|
30
|
+
<div>
|
|
31
|
+
<Select name={modePath}
|
|
32
|
+
value={modeValue ?? "date"}
|
|
33
|
+
error={Boolean(modeError)}
|
|
34
|
+
onValueChange={(v) => setFieldValue(modePath, v)}
|
|
35
|
+
label={"Mode"}
|
|
36
|
+
renderValue={(v) => {
|
|
37
|
+
switch (v) {
|
|
38
|
+
case "date_time":
|
|
39
|
+
return "Date/Time";
|
|
40
|
+
case "date":
|
|
41
|
+
return "Date";
|
|
42
|
+
default:
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
}}
|
|
46
|
+
disabled={disabled}>
|
|
47
|
+
<SelectItem value={"date_time"}> Date/Time </SelectItem>
|
|
48
|
+
<SelectItem value={"date"}> Date </SelectItem>
|
|
49
|
+
</Select>
|
|
50
|
+
<FieldCaption error={Boolean(modeError)}>
|
|
51
|
+
{modeError}
|
|
52
|
+
</FieldCaption>
|
|
53
|
+
</div>
|
|
54
|
+
<div>
|
|
55
|
+
<Select name={autoValuePath}
|
|
56
|
+
disabled={disabled}
|
|
57
|
+
value={autoValueValue ?? ""}
|
|
58
|
+
onValueChange={(v) => setFieldValue(autoValuePath, v === "none" ? null : v)}
|
|
59
|
+
renderValue={(v) => {
|
|
60
|
+
switch (v) {
|
|
61
|
+
case "on_create":
|
|
62
|
+
return "On create";
|
|
63
|
+
case "on_update":
|
|
64
|
+
return "On any update";
|
|
65
|
+
default:
|
|
66
|
+
return "None";
|
|
67
|
+
}
|
|
68
|
+
}}
|
|
69
|
+
error={Boolean(autoValueError)}
|
|
70
|
+
label={"Automatic value"}>
|
|
71
|
+
<SelectItem value={"none"}> None </SelectItem>
|
|
72
|
+
<SelectItem value={"on_create"}> On create </SelectItem>
|
|
73
|
+
<SelectItem value={"on_update"}> On any update </SelectItem>
|
|
74
|
+
</Select>
|
|
75
|
+
<FieldCaption error={Boolean(autoValueError)}>
|
|
76
|
+
{autoValueError ?? "Update this field automatically when creating or updating the entity"}
|
|
77
|
+
</FieldCaption>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div className={"col-span-12"}>
|
|
83
|
+
<ValidationPanel>
|
|
84
|
+
<GeneralPropertyValidation disabled={disabled}/>
|
|
85
|
+
</ValidationPanel>
|
|
86
|
+
</div>
|
|
87
|
+
</>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { getIn, useFormex } from "@firecms/formex";
|
|
3
|
+
import { EnumValueConfig, resolveEnumValues, useSnackbarController } from "@firecms/core";
|
|
4
|
+
import { Select, SelectItem } from "@firecms/ui";
|
|
5
|
+
import { EnumForm } from "../EnumForm";
|
|
6
|
+
import { StringPropertyValidation } from "./validation/StringPropertyValidation";
|
|
7
|
+
import { ArrayPropertyValidation } from "./validation/ArrayPropertyValidation";
|
|
8
|
+
import { ValidationPanel } from "./validation/ValidationPanel";
|
|
9
|
+
import { PropertyWithId } from "../PropertyEditView";
|
|
10
|
+
|
|
11
|
+
export function EnumPropertyField({
|
|
12
|
+
multiselect,
|
|
13
|
+
updateIds,
|
|
14
|
+
disabled,
|
|
15
|
+
showErrors,
|
|
16
|
+
allowDataInference,
|
|
17
|
+
getData
|
|
18
|
+
}: {
|
|
19
|
+
multiselect: boolean;
|
|
20
|
+
updateIds: boolean;
|
|
21
|
+
disabled: boolean;
|
|
22
|
+
showErrors: boolean;
|
|
23
|
+
allowDataInference?: boolean;
|
|
24
|
+
getData?: () => Promise<object[]>;
|
|
25
|
+
}) {
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
values,
|
|
29
|
+
setFieldError,
|
|
30
|
+
setFieldValue
|
|
31
|
+
} = useFormex<PropertyWithId>();
|
|
32
|
+
|
|
33
|
+
const snackbarContext = useSnackbarController();
|
|
34
|
+
|
|
35
|
+
const enumValuesPath = multiselect ? "of.enumValues" : "enumValues";
|
|
36
|
+
|
|
37
|
+
const defaultValue = getIn(values, "defaultValue");
|
|
38
|
+
|
|
39
|
+
const valuesEnumValues = getIn(values, enumValuesPath);
|
|
40
|
+
const enumValues: EnumValueConfig[] = useMemo(() => {
|
|
41
|
+
if (!valuesEnumValues || typeof valuesEnumValues === "boolean")
|
|
42
|
+
return [] as EnumValueConfig[];
|
|
43
|
+
return resolveEnumValues(valuesEnumValues) ?? [] as EnumValueConfig[];
|
|
44
|
+
}, [valuesEnumValues]);
|
|
45
|
+
|
|
46
|
+
const onValuesChanged = (value: EnumValueConfig[]) => {
|
|
47
|
+
if (!values)
|
|
48
|
+
return;
|
|
49
|
+
setFieldValue(enumValuesPath, value);
|
|
50
|
+
if (!multiselect) {
|
|
51
|
+
const enumIds = value.filter(v => Boolean(v?.id)).map((v: any) => v.id);
|
|
52
|
+
if (defaultValue && !enumIds.includes(defaultValue)) {
|
|
53
|
+
setFieldValue("defaultValue", undefined);
|
|
54
|
+
snackbarContext.open({
|
|
55
|
+
type: "warning",
|
|
56
|
+
message: "Default value was cleared"
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
<div className={"col-span-12"}>
|
|
65
|
+
<EnumForm enumValues={enumValues}
|
|
66
|
+
updateIds={updateIds}
|
|
67
|
+
disabled={disabled}
|
|
68
|
+
allowDataInference={allowDataInference}
|
|
69
|
+
onError={(hasError) => {
|
|
70
|
+
setFieldError(enumValuesPath, hasError ? "This enum property is missing some values" : undefined);
|
|
71
|
+
}}
|
|
72
|
+
getData={getData
|
|
73
|
+
? () => getData()
|
|
74
|
+
.then(res => res.map(entry => values.id && getIn(entry, values.id)).filter(Boolean))
|
|
75
|
+
: undefined}
|
|
76
|
+
onValuesChanged={onValuesChanged}/>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div className={"col-span-12"}>
|
|
80
|
+
|
|
81
|
+
<ValidationPanel>
|
|
82
|
+
{!multiselect &&
|
|
83
|
+
<StringPropertyValidation disabled={disabled}
|
|
84
|
+
showErrors={showErrors}/>}
|
|
85
|
+
{multiselect &&
|
|
86
|
+
<ArrayPropertyValidation disabled={disabled}/>}
|
|
87
|
+
</ValidationPanel>
|
|
88
|
+
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
{!multiselect && <div className={"col-span-12"}>
|
|
92
|
+
|
|
93
|
+
<Select
|
|
94
|
+
disabled={disabled}
|
|
95
|
+
position={"item-aligned"}
|
|
96
|
+
onValueChange={(value: string) => {
|
|
97
|
+
setFieldValue("defaultValue", value);
|
|
98
|
+
}}
|
|
99
|
+
label={"Default value"}
|
|
100
|
+
value={defaultValue ?? ""}>
|
|
101
|
+
{enumValues
|
|
102
|
+
.filter((enumValue) => Boolean(enumValue?.id))
|
|
103
|
+
.map((enumValue) => (
|
|
104
|
+
<SelectItem key={enumValue.id}
|
|
105
|
+
value={enumValue.id?.toString()}>
|
|
106
|
+
{enumValue.label}
|
|
107
|
+
</SelectItem>
|
|
108
|
+
))}
|
|
109
|
+
</Select>
|
|
110
|
+
|
|
111
|
+
</div>}
|
|
112
|
+
</>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ValidationPanel } from "./validation/ValidationPanel";
|
|
3
|
+
import { GeneralPropertyValidation } from "./validation/GeneralPropertyValidation";
|
|
4
|
+
|
|
5
|
+
export function KeyValuePropertyField({ disabled }: {
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
}) {
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<>
|
|
11
|
+
<div className={"col-span-12"}>
|
|
12
|
+
|
|
13
|
+
<ValidationPanel>
|
|
14
|
+
<GeneralPropertyValidation disabled={disabled}/>
|
|
15
|
+
</ValidationPanel>
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
</>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { FieldCaption, MapProperty, Property, PropertyConfig, } from "@firecms/core";
|
|
3
|
+
import { AddIcon, BooleanSwitchWithLabel, Button, Paper, Typography } from "@firecms/ui";
|
|
4
|
+
import { PropertyFormDialog } from "../PropertyEditView";
|
|
5
|
+
import { getIn, useFormex } from "@firecms/formex";
|
|
6
|
+
import { PropertyTree } from "../PropertyTree";
|
|
7
|
+
import { getFullId, idToPropertiesPath, namespaceToPropertiesOrderPath, namespaceToPropertiesPath } from "../util";
|
|
8
|
+
|
|
9
|
+
export function MapPropertyField({ disabled, getData, allowDataInference, propertyConfigs, collectionEditable }: {
|
|
10
|
+
disabled: boolean;
|
|
11
|
+
getData?: () => Promise<object[]>;
|
|
12
|
+
allowDataInference: boolean;
|
|
13
|
+
propertyConfigs: Record<string, PropertyConfig>,
|
|
14
|
+
collectionEditable: boolean;
|
|
15
|
+
}) {
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
values,
|
|
19
|
+
setFieldValue
|
|
20
|
+
} = useFormex<MapProperty>();
|
|
21
|
+
|
|
22
|
+
const [propertyDialogOpen, setPropertyDialogOpen] = useState<boolean>(false);
|
|
23
|
+
const [selectedPropertyKey, setSelectedPropertyKey] = useState<string | undefined>();
|
|
24
|
+
const [selectedPropertyNamespace, setSelectedPropertyNamespace] = useState<string | undefined>();
|
|
25
|
+
|
|
26
|
+
const propertiesOrder = values.propertiesOrder ?? Object.keys(values.properties ?? {});
|
|
27
|
+
const onPropertyCreated = ({
|
|
28
|
+
id,
|
|
29
|
+
property
|
|
30
|
+
}: { id?: string, property: Property }) => {
|
|
31
|
+
if (!id)
|
|
32
|
+
throw Error();
|
|
33
|
+
setFieldValue("properties", {
|
|
34
|
+
...(values.properties ?? {}),
|
|
35
|
+
[id]: property
|
|
36
|
+
}, false);
|
|
37
|
+
|
|
38
|
+
const currentPropertiesOrder = values.propertiesOrder ?? Object.keys(values.properties ?? {});
|
|
39
|
+
const newPropertiesOrder = currentPropertiesOrder.includes(id) ? currentPropertiesOrder : [...currentPropertiesOrder, id];
|
|
40
|
+
setFieldValue("propertiesOrder", newPropertiesOrder, false);
|
|
41
|
+
|
|
42
|
+
setPropertyDialogOpen(false);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const deleteProperty = (propertyKey?: string, namespace?: string) => {
|
|
46
|
+
const fullId = propertyKey ? getFullId(propertyKey, namespace) : undefined;
|
|
47
|
+
if (!fullId)
|
|
48
|
+
throw Error("collection editor miss config");
|
|
49
|
+
|
|
50
|
+
const propertiesPath = idToPropertiesPath(fullId);
|
|
51
|
+
const propertiesOrderPath = namespaceToPropertiesOrderPath(namespace);
|
|
52
|
+
|
|
53
|
+
const currentPropertiesOrder: string[] = getIn(values, propertiesOrderPath) ?? Object.keys(getIn(values, namespaceToPropertiesPath(namespace)));
|
|
54
|
+
|
|
55
|
+
setFieldValue(propertiesPath, undefined, false);
|
|
56
|
+
setFieldValue(propertiesOrderPath, currentPropertiesOrder.filter((p) => p !== propertyKey), false);
|
|
57
|
+
|
|
58
|
+
setPropertyDialogOpen(false);
|
|
59
|
+
setSelectedPropertyKey(undefined);
|
|
60
|
+
setSelectedPropertyNamespace(undefined);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const selectedPropertyFullId = selectedPropertyKey ? getFullId(selectedPropertyKey, selectedPropertyNamespace) : undefined;
|
|
64
|
+
const selectedProperty = selectedPropertyFullId ? getIn(values.properties, selectedPropertyFullId.replaceAll(".", ".properties.")) : undefined;
|
|
65
|
+
|
|
66
|
+
const empty = !propertiesOrder || propertiesOrder.length < 1;
|
|
67
|
+
|
|
68
|
+
const onPropertyMove = (propertiesOrder: string[], namespace?: string) => {
|
|
69
|
+
setFieldValue(namespaceToPropertiesOrderPath(namespace), propertiesOrder, false);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<>
|
|
74
|
+
<div className={"col-span-12"}>
|
|
75
|
+
<div className="flex justify-between items-end my-4">
|
|
76
|
+
<Typography variant={"subtitle2"}>Properties in this group</Typography>
|
|
77
|
+
<Button
|
|
78
|
+
color="primary"
|
|
79
|
+
variant={"outlined"}
|
|
80
|
+
onClick={() => setPropertyDialogOpen(true)}
|
|
81
|
+
startIcon={<AddIcon/>}
|
|
82
|
+
>
|
|
83
|
+
Add property to {values.name ?? "this group"}
|
|
84
|
+
</Button>
|
|
85
|
+
</div>
|
|
86
|
+
<Paper className="p-2 pl-8">
|
|
87
|
+
<PropertyTree
|
|
88
|
+
properties={values.properties ?? {}}
|
|
89
|
+
propertiesOrder={propertiesOrder}
|
|
90
|
+
errors={{}}
|
|
91
|
+
collectionEditable={collectionEditable}
|
|
92
|
+
onPropertyClick={(propertyKey, namespace) => {
|
|
93
|
+
setSelectedPropertyKey(propertyKey);
|
|
94
|
+
setSelectedPropertyNamespace(namespace);
|
|
95
|
+
setPropertyDialogOpen(true);
|
|
96
|
+
}}
|
|
97
|
+
onPropertyMove={onPropertyMove}/>
|
|
98
|
+
|
|
99
|
+
{empty &&
|
|
100
|
+
<Typography variant={"label"}
|
|
101
|
+
className="h-full flex items-center justify-center p-4">
|
|
102
|
+
Add the first property to this group
|
|
103
|
+
</Typography>}
|
|
104
|
+
</Paper>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div className={"col-span-12"}>
|
|
108
|
+
<BooleanSwitchWithLabel
|
|
109
|
+
position={"start"}
|
|
110
|
+
size={"small"}
|
|
111
|
+
label="Spread children as columns"
|
|
112
|
+
onValueChange={(v) => setFieldValue("spreadChildren", v)}
|
|
113
|
+
value={values.spreadChildren ?? false}
|
|
114
|
+
/>
|
|
115
|
+
<FieldCaption>
|
|
116
|
+
Set this flag to true if you want to display the children of this group as individual columns.
|
|
117
|
+
</FieldCaption>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<PropertyFormDialog
|
|
121
|
+
inArray={false}
|
|
122
|
+
forceShowErrors={false}
|
|
123
|
+
open={propertyDialogOpen}
|
|
124
|
+
allowDataInference={allowDataInference}
|
|
125
|
+
collectionEditable={collectionEditable}
|
|
126
|
+
onCancel={() => {
|
|
127
|
+
setPropertyDialogOpen(false);
|
|
128
|
+
setSelectedPropertyKey(undefined);
|
|
129
|
+
setSelectedPropertyNamespace(undefined);
|
|
130
|
+
}}
|
|
131
|
+
onOkClicked={() => {
|
|
132
|
+
setPropertyDialogOpen(false);
|
|
133
|
+
setSelectedPropertyKey(undefined);
|
|
134
|
+
setSelectedPropertyNamespace(undefined);
|
|
135
|
+
}}
|
|
136
|
+
getData={getData}
|
|
137
|
+
onDelete={deleteProperty}
|
|
138
|
+
propertyKey={selectedPropertyKey}
|
|
139
|
+
propertyNamespace={selectedPropertyNamespace}
|
|
140
|
+
property={selectedProperty}
|
|
141
|
+
existingProperty={Boolean(selectedPropertyKey)}
|
|
142
|
+
autoUpdateId={!selectedPropertyKey}
|
|
143
|
+
autoOpenTypeSelect={!selectedPropertyKey}
|
|
144
|
+
onPropertyChanged={onPropertyCreated}
|
|
145
|
+
existingPropertyKeys={selectedPropertyKey ? undefined : propertiesOrder}
|
|
146
|
+
propertyConfigs={propertyConfigs}
|
|
147
|
+
/>
|
|
148
|
+
|
|
149
|
+
</>);
|
|
150
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StringPropertyValidation } from "./validation/StringPropertyValidation";
|
|
3
|
+
import { ValidationPanel } from "./validation/ValidationPanel";
|
|
4
|
+
import { Field, getIn, useFormex } from "@firecms/formex";
|
|
5
|
+
|
|
6
|
+
import { DebouncedTextField, ExpandablePanel, FileUploadIcon, TextField, Typography } from "@firecms/ui";
|
|
7
|
+
|
|
8
|
+
export function MarkdownPropertyField({
|
|
9
|
+
disabled,
|
|
10
|
+
showErrors
|
|
11
|
+
}: {
|
|
12
|
+
disabled: boolean;
|
|
13
|
+
showErrors: boolean;
|
|
14
|
+
}) {
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
values,
|
|
18
|
+
setFieldValue
|
|
19
|
+
} = useFormex();
|
|
20
|
+
|
|
21
|
+
const baseStoragePath = "storage";
|
|
22
|
+
|
|
23
|
+
const metadata = `${baseStoragePath}.metadata`;
|
|
24
|
+
const fileName = `${baseStoragePath}.fileName`;
|
|
25
|
+
const maxSize = `${baseStoragePath}.maxSize`;
|
|
26
|
+
const storagePath = `${baseStoragePath}.storagePath`;
|
|
27
|
+
|
|
28
|
+
const fileNameValue = getIn(values, fileName) ?? "{rand}_{file}";
|
|
29
|
+
const storagePathValue = getIn(values, storagePath) ?? "/";
|
|
30
|
+
const maxSizeValue = getIn(values, maxSize);
|
|
31
|
+
|
|
32
|
+
const hasFilenameCallback = typeof fileNameValue === "function";
|
|
33
|
+
const hasStoragePathCallback = typeof storagePathValue === "function";
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
<div className={"col-span-12"}>
|
|
38
|
+
|
|
39
|
+
<ValidationPanel>
|
|
40
|
+
|
|
41
|
+
<StringPropertyValidation disabled={disabled}
|
|
42
|
+
length={true}
|
|
43
|
+
lowercase={true}
|
|
44
|
+
max={true}
|
|
45
|
+
min={true}
|
|
46
|
+
trim={true}
|
|
47
|
+
uppercase={true}
|
|
48
|
+
showErrors={showErrors}/>
|
|
49
|
+
|
|
50
|
+
</ValidationPanel>
|
|
51
|
+
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div className={"col-span-12"}>
|
|
55
|
+
<ExpandablePanel
|
|
56
|
+
title={
|
|
57
|
+
<div className="flex flex-row text-gray-500">
|
|
58
|
+
<FileUploadIcon/>
|
|
59
|
+
<Typography variant={"subtitle2"}
|
|
60
|
+
className="ml-2">
|
|
61
|
+
File upload config
|
|
62
|
+
</Typography>
|
|
63
|
+
</div>
|
|
64
|
+
}>
|
|
65
|
+
|
|
66
|
+
<div className={"grid grid-cols-12 gap-2 p-4"}>
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
<div className={"col-span-12"}>
|
|
70
|
+
<Field name={fileName}
|
|
71
|
+
as={DebouncedTextField}
|
|
72
|
+
label={"File name"}
|
|
73
|
+
size={"small"}
|
|
74
|
+
disabled={hasFilenameCallback || disabled}
|
|
75
|
+
value={hasFilenameCallback ? "-" : fileNameValue}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
<div className={"col-span-12"}>
|
|
79
|
+
<Field name={storagePath}
|
|
80
|
+
as={DebouncedTextField}
|
|
81
|
+
label={"Storage path"}
|
|
82
|
+
disabled={hasStoragePathCallback || disabled}
|
|
83
|
+
size={"small"}
|
|
84
|
+
value={hasStoragePathCallback ? "-" : storagePathValue}
|
|
85
|
+
/>
|
|
86
|
+
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
87
|
+
<p>You can use the following placeholders in
|
|
88
|
+
the file name
|
|
89
|
+
and storage path values:</p>
|
|
90
|
+
<ul>
|
|
91
|
+
<li>{"{file} - Full name of the uploaded file"}</li>
|
|
92
|
+
<li>{"{file.name} - Name of the uploaded file without extension"}</li>
|
|
93
|
+
<li>{"{file.ext} - Extension of the uploaded file"}</li>
|
|
94
|
+
<li>{"{entityId} - ID of the entity"}</li>
|
|
95
|
+
<li>{"{propertyKey} - ID of this field"}</li>
|
|
96
|
+
<li>{"{path} - Path of this entity"}</li>
|
|
97
|
+
<li>{"{rand} - Random value used to avoid name collisions"}</li>
|
|
98
|
+
</ul>
|
|
99
|
+
</Typography>
|
|
100
|
+
|
|
101
|
+
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
102
|
+
When using Markdown, the URL of the uploaded files are always saved in the text value
|
|
103
|
+
(not
|
|
104
|
+
the path).
|
|
105
|
+
</Typography>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div className={"col-span-12"}>
|
|
109
|
+
<DebouncedTextField name={maxSize}
|
|
110
|
+
type={"number"}
|
|
111
|
+
label={"Max size (in bytes)"}
|
|
112
|
+
size={"small"}
|
|
113
|
+
value={maxSizeValue !== undefined && maxSizeValue !== null ? maxSizeValue.toString() : ""}
|
|
114
|
+
onChange={(e) => {
|
|
115
|
+
const value = e.target.value;
|
|
116
|
+
if (value === "") setFieldValue(maxSize, undefined);
|
|
117
|
+
else setFieldValue(maxSize, parseInt(value));
|
|
118
|
+
}}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
</div>
|
|
123
|
+
</ExpandablePanel>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div className={"col-span-12"}>
|
|
127
|
+
|
|
128
|
+
<TextField name={"defaultValue"}
|
|
129
|
+
disabled={disabled}
|
|
130
|
+
onChange={(e: any) => {
|
|
131
|
+
setFieldValue("defaultValue", e.target.value === "" ? undefined : e.target.value);
|
|
132
|
+
}}
|
|
133
|
+
label={"Default value"}
|
|
134
|
+
value={getIn(values, "defaultValue") ?? ""}/>
|
|
135
|
+
|
|
136
|
+
</div>
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { NumberPropertyValidation } from "./validation/NumberPropertyValidation";
|
|
3
|
+
import { ValidationPanel } from "./validation/ValidationPanel";
|
|
4
|
+
import { TextField } from "@firecms/ui";
|
|
5
|
+
import { getIn, useFormex } from "@firecms/formex";
|
|
6
|
+
|
|
7
|
+
export function NumberPropertyField({ disabled }: {
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
}) {
|
|
10
|
+
|
|
11
|
+
const { values, setFieldValue } = useFormex();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<>
|
|
15
|
+
|
|
16
|
+
<div className={"col-span-12"}>
|
|
17
|
+
|
|
18
|
+
<ValidationPanel>
|
|
19
|
+
<NumberPropertyValidation disabled={disabled}/>
|
|
20
|
+
</ValidationPanel>
|
|
21
|
+
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div className={"col-span-12"}>
|
|
25
|
+
|
|
26
|
+
<TextField name={"defaultValue"}
|
|
27
|
+
disabled={disabled}
|
|
28
|
+
type={"number"}
|
|
29
|
+
onChange={(e: any) => {
|
|
30
|
+
setFieldValue("defaultValue", e.target.value === "" ? undefined : parseFloat(e.target.value));
|
|
31
|
+
}}
|
|
32
|
+
label={"Default value"}
|
|
33
|
+
value={getIn(values, "defaultValue") ?? ""}/>
|
|
34
|
+
|
|
35
|
+
</div>
|
|
36
|
+
</>
|
|
37
|
+
);
|
|
38
|
+
}
|