@firecms/collection_editor 3.0.0-alpha.9 → 3.0.0-beta.2-pre.1
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/ConfigControllerProvider.d.ts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.es.js +3128 -4094
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +3 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +22 -7
- package/dist/types/collection_inference.d.ts +1 -1
- package/dist/types/config_controller.d.ts +32 -5
- package/dist/types/persisted_collection.d.ts +3 -1
- package/dist/ui/CollectionViewHeaderAction.d.ts +10 -0
- package/dist/{components → ui}/EditorCollectionAction.d.ts +1 -1
- package/dist/ui/MissingReferenceWidget.d.ts +3 -0
- package/dist/ui/NewCollectionButton.d.ts +1 -0
- package/dist/ui/PropertyAddColumnComponent.d.ts +6 -0
- package/dist/ui/RootCollectionSuggestions.d.ts +1 -0
- package/dist/{components → ui}/collection_editor/CollectionDetailsForm.d.ts +3 -2
- package/dist/{components → ui}/collection_editor/CollectionEditorDialog.d.ts +11 -6
- package/dist/{components → ui}/collection_editor/CollectionPropertiesEditorForm.d.ts +6 -3
- package/dist/{components → ui}/collection_editor/CollectionYupValidation.d.ts +3 -0
- package/dist/ui/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
- package/dist/ui/collection_editor/GetCodeDialog.d.ts +5 -0
- package/dist/{components → ui}/collection_editor/PropertyEditView.d.ts +8 -6
- package/dist/{components → ui}/collection_editor/PropertyFieldPreview.d.ts +4 -3
- package/dist/ui/collection_editor/PropertySelectItem.d.ts +8 -0
- package/dist/{components → ui}/collection_editor/PropertyTree.d.ts +8 -5
- package/dist/{components → ui}/collection_editor/SubcollectionsEditTab.d.ts +2 -2
- package/dist/{components → ui}/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
- package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +7 -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 -0
- package/dist/{components → ui}/collection_editor/properties/MapPropertyField.d.ts +4 -1
- 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/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 +5 -3
- package/dist/utils/entities.d.ts +3 -4
- package/package.json +22 -19
- package/src/ConfigControllerProvider.tsx +336 -0
- package/src/index.ts +35 -0
- package/src/types/collection_editor_controller.tsx +42 -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 +43 -0
- package/src/ui/EditorCollectionAction.tsx +109 -0
- package/src/ui/HomePageEditorCollectionAction.tsx +84 -0
- package/src/ui/MissingReferenceWidget.tsx +35 -0
- package/src/ui/NewCollectionButton.tsx +16 -0
- package/src/ui/NewCollectionCard.tsx +47 -0
- package/src/ui/PropertyAddColumnComponent.tsx +42 -0
- package/src/ui/RootCollectionSuggestions.tsx +55 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +366 -0
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +754 -0
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +206 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +481 -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 +354 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +110 -0
- package/src/ui/collection_editor/PropertyEditView.tsx +558 -0
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +203 -0
- package/src/ui/collection_editor/PropertySelectItem.tsx +32 -0
- package/src/ui/collection_editor/PropertyTree.tsx +233 -0
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +253 -0
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +47 -0
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +37 -0
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +260 -0
- package/src/ui/collection_editor/import/clean_import_data.ts +53 -0
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +135 -0
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +36 -0
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +137 -0
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +87 -0
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +117 -0
- package/src/ui/collection_editor/properties/FieldHelperView.tsx +13 -0
- package/src/ui/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +149 -0
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +38 -0
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +165 -0
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +108 -0
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +194 -0
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +79 -0
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +89 -0
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +36 -0
- package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
- package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +50 -0
- package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +100 -0
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +132 -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 +188 -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 +21 -0
- package/src/ui/collection_editor/utils/strings.ts +8 -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 +137 -0
- package/src/useCollectionsConfigController.tsx +9 -0
- package/src/utils/arrays.ts +3 -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/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/CollectionEditorWelcomeView.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/EnumForm.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/FieldHelperView.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/util.d.ts +0 -0
- /package/dist/{components → ui}/collection_editor/utils/useTraceUpdate.d.ts +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import { MapProperty, Property, PropertyConfig, } from "@firecms/core";
|
|
3
|
+
import { AddIcon, BooleanSwitchWithLabel, Button, Paper, Typography } from "@firecms/ui";
|
|
4
|
+
import { PropertyFormDialog } from "../PropertyEditView";
|
|
5
|
+
import { getIn, useFormikContext } from "formik";
|
|
6
|
+
import { PropertyTree } from "../PropertyTree";
|
|
7
|
+
import { getFullId, idToPropertiesPath, namespaceToPropertiesOrderPath, namespaceToPropertiesPath } from "../util";
|
|
8
|
+
import { FieldHelperView } from "./FieldHelperView";
|
|
9
|
+
|
|
10
|
+
export function MapPropertyField({ disabled, getData, allowDataInference, propertyConfigs, collectionEditable }: {
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
getData?: () => Promise<object[]>;
|
|
13
|
+
allowDataInference: boolean;
|
|
14
|
+
propertyConfigs: Record<string, PropertyConfig>,
|
|
15
|
+
collectionEditable: boolean;
|
|
16
|
+
}) {
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
values,
|
|
20
|
+
setFieldValue
|
|
21
|
+
} = useFormikContext<MapProperty>();
|
|
22
|
+
|
|
23
|
+
const [propertyDialogOpen, setPropertyDialogOpen] = useState<boolean>(false);
|
|
24
|
+
const [selectedPropertyKey, setSelectedPropertyKey] = useState<string | undefined>();
|
|
25
|
+
const [selectedPropertyNamespace, setSelectedPropertyNamespace] = useState<string | undefined>();
|
|
26
|
+
|
|
27
|
+
const propertiesOrder = values.propertiesOrder ?? Object.keys(values.properties ?? {});
|
|
28
|
+
const onPropertyCreated = useCallback(({
|
|
29
|
+
id,
|
|
30
|
+
property
|
|
31
|
+
}: { id?: string, property: Property }) => {
|
|
32
|
+
if (!id)
|
|
33
|
+
throw Error();
|
|
34
|
+
setFieldValue("properties", {
|
|
35
|
+
...(values.properties ?? {}),
|
|
36
|
+
[id]: property
|
|
37
|
+
}, false);
|
|
38
|
+
setFieldValue("propertiesOrder", [...propertiesOrder, id], false);
|
|
39
|
+
setPropertyDialogOpen(false);
|
|
40
|
+
}, [values.properties, propertiesOrder]);
|
|
41
|
+
|
|
42
|
+
const deleteProperty = useCallback((propertyKey?: string, namespace?: string) => {
|
|
43
|
+
const fullId = propertyKey ? getFullId(propertyKey, namespace) : undefined;
|
|
44
|
+
if (!fullId)
|
|
45
|
+
throw Error("collection editor miss config");
|
|
46
|
+
|
|
47
|
+
const propertiesPath = idToPropertiesPath(fullId);
|
|
48
|
+
const propertiesOrderPath = namespaceToPropertiesOrderPath(namespace);
|
|
49
|
+
|
|
50
|
+
const currentPropertiesOrder: string[] = getIn(values, propertiesOrderPath) ?? Object.keys(getIn(values, namespaceToPropertiesPath(namespace)));
|
|
51
|
+
|
|
52
|
+
setFieldValue(propertiesPath, undefined, false);
|
|
53
|
+
setFieldValue(propertiesOrderPath, currentPropertiesOrder.filter((p) => p !== propertyKey), false);
|
|
54
|
+
|
|
55
|
+
setPropertyDialogOpen(false);
|
|
56
|
+
setSelectedPropertyKey(undefined);
|
|
57
|
+
setSelectedPropertyNamespace(undefined);
|
|
58
|
+
}, [setFieldValue, values]);
|
|
59
|
+
|
|
60
|
+
const selectedPropertyFullId = selectedPropertyKey ? getFullId(selectedPropertyKey, selectedPropertyNamespace) : undefined;
|
|
61
|
+
const selectedProperty = selectedPropertyFullId ? getIn(values.properties, selectedPropertyFullId.replaceAll(".", ".properties.")) : undefined;
|
|
62
|
+
|
|
63
|
+
const addChildButton = <Button
|
|
64
|
+
color="primary"
|
|
65
|
+
variant={"outlined"}
|
|
66
|
+
onClick={() => setPropertyDialogOpen(true)}
|
|
67
|
+
startIcon={<AddIcon/>}
|
|
68
|
+
>
|
|
69
|
+
Add property to {values.name ?? "this group"}
|
|
70
|
+
</Button>;
|
|
71
|
+
|
|
72
|
+
const empty = !propertiesOrder || propertiesOrder.length < 1;
|
|
73
|
+
|
|
74
|
+
const onPropertyMove = useCallback((propertiesOrder: string[], namespace?: string) => {
|
|
75
|
+
setFieldValue(namespaceToPropertiesOrderPath(namespace), propertiesOrder, false);
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<>
|
|
80
|
+
<div className={"col-span-12"}>
|
|
81
|
+
<div className="flex justify-between items-end my-4">
|
|
82
|
+
<Typography variant={"subtitle2"}>Properties in this group</Typography>
|
|
83
|
+
{addChildButton}
|
|
84
|
+
</div>
|
|
85
|
+
<Paper className="p-2 pl-8">
|
|
86
|
+
<PropertyTree
|
|
87
|
+
properties={values.properties ?? {}}
|
|
88
|
+
propertiesOrder={propertiesOrder}
|
|
89
|
+
errors={{}}
|
|
90
|
+
collectionEditable={collectionEditable}
|
|
91
|
+
onPropertyClick={(propertyKey, namespace) => {
|
|
92
|
+
setSelectedPropertyKey(propertyKey);
|
|
93
|
+
setSelectedPropertyNamespace(namespace);
|
|
94
|
+
setPropertyDialogOpen(true);
|
|
95
|
+
}}
|
|
96
|
+
onPropertyMove={onPropertyMove}/>
|
|
97
|
+
|
|
98
|
+
{empty &&
|
|
99
|
+
<Typography variant={"label"}
|
|
100
|
+
className="h-full flex items-center justify-center p-4">
|
|
101
|
+
Add the first property to this group
|
|
102
|
+
</Typography>}
|
|
103
|
+
</Paper>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div className={"col-span-12"}>
|
|
107
|
+
<BooleanSwitchWithLabel
|
|
108
|
+
position={"start"}
|
|
109
|
+
size={"small"}
|
|
110
|
+
label="Spread children as columns"
|
|
111
|
+
onValueChange={(v) => setFieldValue("spreadChildren", v)}
|
|
112
|
+
value={values.spreadChildren ?? false}
|
|
113
|
+
/>
|
|
114
|
+
<FieldHelperView>
|
|
115
|
+
Set this flag to true if you want to display the children of this group as individual columns.
|
|
116
|
+
</FieldHelperView>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<PropertyFormDialog
|
|
120
|
+
inArray={false}
|
|
121
|
+
forceShowErrors={false}
|
|
122
|
+
open={propertyDialogOpen}
|
|
123
|
+
allowDataInference={allowDataInference}
|
|
124
|
+
collectionEditable={collectionEditable}
|
|
125
|
+
onCancel={() => {
|
|
126
|
+
setPropertyDialogOpen(false);
|
|
127
|
+
setSelectedPropertyKey(undefined);
|
|
128
|
+
setSelectedPropertyNamespace(undefined);
|
|
129
|
+
}}
|
|
130
|
+
onOkClicked={() => {
|
|
131
|
+
setPropertyDialogOpen(false);
|
|
132
|
+
setSelectedPropertyKey(undefined);
|
|
133
|
+
setSelectedPropertyNamespace(undefined);
|
|
134
|
+
}}
|
|
135
|
+
getData={getData}
|
|
136
|
+
onDelete={deleteProperty}
|
|
137
|
+
propertyKey={selectedPropertyKey}
|
|
138
|
+
propertyNamespace={selectedPropertyNamespace}
|
|
139
|
+
property={selectedProperty}
|
|
140
|
+
existingProperty={Boolean(selectedPropertyKey)}
|
|
141
|
+
autoUpdateId={!selectedPropertyKey}
|
|
142
|
+
autoOpenTypeSelect={!selectedPropertyKey}
|
|
143
|
+
onPropertyChanged={onPropertyCreated}
|
|
144
|
+
existingPropertyKeys={selectedPropertyKey ? undefined : propertiesOrder}
|
|
145
|
+
propertyConfigs={propertyConfigs}
|
|
146
|
+
/>
|
|
147
|
+
|
|
148
|
+
</>);
|
|
149
|
+
}
|
|
@@ -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, useFormikContext } from "formik";
|
|
6
|
+
|
|
7
|
+
export function NumberPropertyField({ disabled }: {
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
}) {
|
|
10
|
+
|
|
11
|
+
const { values, setFieldValue } = useFormikContext();
|
|
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
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Field, getIn, useFormikContext } from "formik";
|
|
3
|
+
import { IconForView, NumberProperty, StringProperty, useNavigationController } from "@firecms/core";
|
|
4
|
+
import { CircularProgress, Select, SelectGroup, SelectItem, Typography, } from "@firecms/ui";
|
|
5
|
+
import { FieldHelperView } from "./FieldHelperView";
|
|
6
|
+
|
|
7
|
+
export function ReferencePropertyField({
|
|
8
|
+
existing,
|
|
9
|
+
multiple,
|
|
10
|
+
disabled,
|
|
11
|
+
showErrors
|
|
12
|
+
}: { existing: boolean, multiple: boolean, disabled: boolean, showErrors: boolean }) {
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
values,
|
|
16
|
+
handleChange,
|
|
17
|
+
errors,
|
|
18
|
+
} = useFormikContext<StringProperty | NumberProperty>();
|
|
19
|
+
|
|
20
|
+
const navigation = useNavigationController();
|
|
21
|
+
|
|
22
|
+
if (!navigation)
|
|
23
|
+
return <div className={"col-span-12"}>
|
|
24
|
+
<CircularProgress/>
|
|
25
|
+
</div>;
|
|
26
|
+
|
|
27
|
+
const pathPath = multiple ? "of.path" : "path";
|
|
28
|
+
const pathValue: string | undefined = getIn(values, pathPath);
|
|
29
|
+
const pathError: string | undefined = showErrors && getIn(errors, pathPath);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<div className={"col-span-12"}>
|
|
34
|
+
<Field required
|
|
35
|
+
name={pathPath}
|
|
36
|
+
pathPath={pathPath}
|
|
37
|
+
type="select"
|
|
38
|
+
validate={validatePath}
|
|
39
|
+
disabled={existing || disabled}
|
|
40
|
+
value={pathValue}
|
|
41
|
+
error={pathError}
|
|
42
|
+
handleChange={handleChange}
|
|
43
|
+
component={CollectionsSelect}/>
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
</>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function validatePath(value?: string) {
|
|
52
|
+
let error;
|
|
53
|
+
if (!value) {
|
|
54
|
+
error = "You must specify a target collection for the field";
|
|
55
|
+
}
|
|
56
|
+
return error;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function CollectionsSelect({
|
|
60
|
+
disabled,
|
|
61
|
+
pathPath,
|
|
62
|
+
value,
|
|
63
|
+
handleChange,
|
|
64
|
+
error,
|
|
65
|
+
...props
|
|
66
|
+
}: {
|
|
67
|
+
disabled: boolean,
|
|
68
|
+
pathPath: string,
|
|
69
|
+
value?: string,
|
|
70
|
+
handleChange: (event: any) => void,
|
|
71
|
+
error?: string
|
|
72
|
+
}) {
|
|
73
|
+
|
|
74
|
+
const navigation = useNavigationController();
|
|
75
|
+
|
|
76
|
+
if (!navigation)
|
|
77
|
+
return <div className={"col-span-12"}>
|
|
78
|
+
<CircularProgress/>
|
|
79
|
+
</div>;
|
|
80
|
+
|
|
81
|
+
const collections = navigation?.collections ?? [];
|
|
82
|
+
|
|
83
|
+
const groups: string[] = Array.from(new Set(
|
|
84
|
+
Object.values(collections).map(e => e.group).filter(Boolean) as string[]
|
|
85
|
+
).values());
|
|
86
|
+
|
|
87
|
+
const ungroupedCollections = collections.filter((col) => !col.group);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<>
|
|
91
|
+
<Select
|
|
92
|
+
error={Boolean(error)}
|
|
93
|
+
disabled={disabled}
|
|
94
|
+
value={value ?? ""}
|
|
95
|
+
position={"item-aligned"}
|
|
96
|
+
name={pathPath}
|
|
97
|
+
onChange={handleChange}
|
|
98
|
+
label={"Target collection"}
|
|
99
|
+
renderValue={(selected) => {
|
|
100
|
+
const selectedCollection = collections.find(collection => collection.id === selected || collection.path === selected);
|
|
101
|
+
if (!selectedCollection) return null;
|
|
102
|
+
return (
|
|
103
|
+
<div className="flex flex-row">
|
|
104
|
+
<IconForView collectionOrView={selectedCollection}/>
|
|
105
|
+
<Typography
|
|
106
|
+
variant={"subtitle2"}
|
|
107
|
+
className="font-medium ml-4">
|
|
108
|
+
{selectedCollection?.name.toUpperCase()}
|
|
109
|
+
</Typography>
|
|
110
|
+
</div>)
|
|
111
|
+
}}
|
|
112
|
+
{...props}>
|
|
113
|
+
|
|
114
|
+
{groups.flatMap((group) => (
|
|
115
|
+
<SelectGroup label={group || "Views"}
|
|
116
|
+
key={`group_${group}`}>
|
|
117
|
+
{
|
|
118
|
+
collections.filter(collection => collection.group === group)
|
|
119
|
+
.map((collection) => {
|
|
120
|
+
return <SelectItem
|
|
121
|
+
key={`${collection.id ?? collection.path}-${group}`}
|
|
122
|
+
value={collection.id ?? collection.path}>
|
|
123
|
+
<div className="flex flex-row">
|
|
124
|
+
<IconForView collectionOrView={collection}/>
|
|
125
|
+
<Typography
|
|
126
|
+
variant={"subtitle2"}
|
|
127
|
+
className="font-medium ml-4">
|
|
128
|
+
{collection?.name.toUpperCase()}
|
|
129
|
+
</Typography>
|
|
130
|
+
</div>
|
|
131
|
+
</SelectItem>;
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
</SelectGroup>
|
|
136
|
+
))}
|
|
137
|
+
|
|
138
|
+
{ungroupedCollections && <SelectGroup label={"Views"}>
|
|
139
|
+
{ungroupedCollections
|
|
140
|
+
.map((collection) => {
|
|
141
|
+
return <SelectItem key={collection.id ?? collection.path}
|
|
142
|
+
value={collection.id ?? collection.path}>
|
|
143
|
+
<div className="flex flex-row">
|
|
144
|
+
<IconForView collectionOrView={collection}/>
|
|
145
|
+
<Typography
|
|
146
|
+
variant={"subtitle2"}
|
|
147
|
+
className="font-medium ml-4">
|
|
148
|
+
{collection?.name.toUpperCase()}
|
|
149
|
+
</Typography>
|
|
150
|
+
</div>
|
|
151
|
+
</SelectItem>;
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
</SelectGroup>}
|
|
156
|
+
|
|
157
|
+
</Select>
|
|
158
|
+
|
|
159
|
+
<FieldHelperView>
|
|
160
|
+
You can only edit the reference collection upon field
|
|
161
|
+
creation.
|
|
162
|
+
</FieldHelperView>
|
|
163
|
+
</>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import { ArrayProperty, getFieldConfig, Property, PropertyConfig } from "@firecms/core";
|
|
3
|
+
import { Button, Paper, Typography } from "@firecms/ui";
|
|
4
|
+
import { Field, getIn, useFormikContext } from "formik";
|
|
5
|
+
import { PropertyFormDialog } from "../PropertyEditView";
|
|
6
|
+
import { PropertyFieldPreview } from "../PropertyFieldPreview";
|
|
7
|
+
import { ArrayPropertyValidation } from "./validation/ArrayPropertyValidation";
|
|
8
|
+
import { ValidationPanel } from "./validation/ValidationPanel";
|
|
9
|
+
|
|
10
|
+
export function RepeatPropertyField({
|
|
11
|
+
showErrors,
|
|
12
|
+
existing,
|
|
13
|
+
disabled,
|
|
14
|
+
getData,
|
|
15
|
+
allowDataInference,
|
|
16
|
+
propertyConfigs,
|
|
17
|
+
collectionEditable
|
|
18
|
+
}: {
|
|
19
|
+
showErrors: boolean,
|
|
20
|
+
existing: boolean,
|
|
21
|
+
disabled: boolean,
|
|
22
|
+
getData?: () => Promise<object[]>;
|
|
23
|
+
allowDataInference: boolean;
|
|
24
|
+
propertyConfigs: Record<string, PropertyConfig>,
|
|
25
|
+
collectionEditable: boolean;
|
|
26
|
+
}) {
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
values,
|
|
30
|
+
handleChange,
|
|
31
|
+
errors,
|
|
32
|
+
setFieldValue,
|
|
33
|
+
touched
|
|
34
|
+
} = useFormikContext<ArrayProperty>();
|
|
35
|
+
|
|
36
|
+
const [propertyDialogOpen, setPropertyDialogOpen] = useState(false);
|
|
37
|
+
const ofProperty = getIn(values, "of");
|
|
38
|
+
const ofPropertyError = getIn(touched, "of") && getIn(errors, "of");
|
|
39
|
+
|
|
40
|
+
const onPropertyChanged = useCallback(({ id, property, namespace }:
|
|
41
|
+
{ id?: string, property: Property, namespace?: string }) => {
|
|
42
|
+
setFieldValue("of", property);
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
const widget = ofProperty && getFieldConfig(ofProperty, propertyConfigs);
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<div className={"col-span-12"}>
|
|
49
|
+
<Typography variant={"subtitle2"}>
|
|
50
|
+
Repeat component
|
|
51
|
+
</Typography>
|
|
52
|
+
<Field
|
|
53
|
+
name={"of"}
|
|
54
|
+
value={ofProperty}
|
|
55
|
+
validate={(property: Property) => {
|
|
56
|
+
return property?.dataType ? undefined : "You need to specify a repeat field";
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{() => (
|
|
60
|
+
<Paper className="p-2 mt-4">
|
|
61
|
+
|
|
62
|
+
{ofProperty && <PropertyFieldPreview
|
|
63
|
+
property={ofProperty}
|
|
64
|
+
onClick={disabled ? undefined : () => setPropertyDialogOpen(true)}
|
|
65
|
+
includeName={false}
|
|
66
|
+
includeEditButton={true}
|
|
67
|
+
selected={false}
|
|
68
|
+
hasError={false}/>}
|
|
69
|
+
|
|
70
|
+
{!disabled && !ofProperty && <Button variant={"text"}
|
|
71
|
+
size={"large"}
|
|
72
|
+
color={ofPropertyError ? "error" : "primary"}
|
|
73
|
+
onClick={() => setPropertyDialogOpen(true)}>
|
|
74
|
+
Edit {`${widget ? widget.name : "repeat component"}`}
|
|
75
|
+
</Button>}
|
|
76
|
+
|
|
77
|
+
<PropertyFormDialog
|
|
78
|
+
inArray={true}
|
|
79
|
+
open={propertyDialogOpen}
|
|
80
|
+
existingProperty={existing}
|
|
81
|
+
getData={getData}
|
|
82
|
+
autoUpdateId={!existing}
|
|
83
|
+
autoOpenTypeSelect={!existing}
|
|
84
|
+
onOkClicked={() => setPropertyDialogOpen(false)}
|
|
85
|
+
allowDataInference={allowDataInference}
|
|
86
|
+
property={ofProperty}
|
|
87
|
+
includeIdAndName={false}
|
|
88
|
+
onPropertyChanged={onPropertyChanged}
|
|
89
|
+
forceShowErrors={showErrors}
|
|
90
|
+
propertyConfigs={propertyConfigs}
|
|
91
|
+
collectionEditable={collectionEditable}
|
|
92
|
+
/>
|
|
93
|
+
</Paper>
|
|
94
|
+
)}
|
|
95
|
+
</Field>
|
|
96
|
+
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div className={"col-span-12"}>
|
|
100
|
+
|
|
101
|
+
<ValidationPanel>
|
|
102
|
+
<ArrayPropertyValidation disabled={disabled}/>
|
|
103
|
+
</ValidationPanel>
|
|
104
|
+
|
|
105
|
+
</div>
|
|
106
|
+
</>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SwitchControl, } from "@firecms/core";
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
Checkbox,
|
|
6
|
+
DebouncedTextField,
|
|
7
|
+
ExpandablePanel,
|
|
8
|
+
FileUploadIcon,
|
|
9
|
+
MultiSelect,
|
|
10
|
+
MultiSelectItem,
|
|
11
|
+
Typography
|
|
12
|
+
} from "@firecms/ui";
|
|
13
|
+
|
|
14
|
+
import { Field, getIn, useFormikContext } from "formik";
|
|
15
|
+
import { GeneralPropertyValidation } from "./validation/GeneralPropertyValidation";
|
|
16
|
+
import { ArrayPropertyValidation } from "./validation/ArrayPropertyValidation";
|
|
17
|
+
import { ValidationPanel } from "./validation/ValidationPanel";
|
|
18
|
+
|
|
19
|
+
const fileTypes: Record<string, string> = {
|
|
20
|
+
"image/*": "Images",
|
|
21
|
+
"video/*": "Videos",
|
|
22
|
+
"audio/*": "Audio files",
|
|
23
|
+
"application/*": "Files (pdf, zip, csv, excel...)",
|
|
24
|
+
"text/*": "Text files"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function StoragePropertyField({
|
|
28
|
+
multiple,
|
|
29
|
+
existing,
|
|
30
|
+
disabled
|
|
31
|
+
}: {
|
|
32
|
+
multiple: boolean;
|
|
33
|
+
existing: boolean;
|
|
34
|
+
disabled: boolean;
|
|
35
|
+
}) {
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
values,
|
|
39
|
+
setFieldValue
|
|
40
|
+
} = useFormikContext();
|
|
41
|
+
|
|
42
|
+
const baseStoragePath = multiple ? "of.storage" : "storage";
|
|
43
|
+
const acceptedFiles = `${baseStoragePath}.acceptedFiles`;
|
|
44
|
+
|
|
45
|
+
const metadata = `${baseStoragePath}.metadata`;
|
|
46
|
+
const fileName = `${baseStoragePath}.fileName`;
|
|
47
|
+
const storagePath = `${baseStoragePath}.storagePath`;
|
|
48
|
+
const storeUrl = `${baseStoragePath}.storeUrl`;
|
|
49
|
+
|
|
50
|
+
const fileNameValue = getIn(values, fileName) ?? "{rand}_{file}";
|
|
51
|
+
const storagePathValue = getIn(values, storagePath) ?? "/";
|
|
52
|
+
|
|
53
|
+
const storedValue = getIn(values, acceptedFiles);
|
|
54
|
+
const fileTypesValue: string[] | undefined = Array.isArray(storedValue) ? storedValue : undefined;
|
|
55
|
+
const allFileTypesSelected = !fileTypesValue || fileTypesValue.length === 0;
|
|
56
|
+
|
|
57
|
+
const handleTypesChange = (value: string[]) => {
|
|
58
|
+
if (!value) setFieldValue(acceptedFiles, undefined);
|
|
59
|
+
else if (value.includes("all")) setFieldValue(acceptedFiles, undefined);
|
|
60
|
+
else if (value.length >= Object.keys(fileTypes).length) setFieldValue(acceptedFiles, undefined);
|
|
61
|
+
else if (allFileTypesSelected)
|
|
62
|
+
setFieldValue(acceptedFiles, Object.keys(fileTypes).filter((v) => !value.includes(v)));
|
|
63
|
+
else setFieldValue(acceptedFiles, value);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const hasFilenameCallback = typeof fileNameValue === "function";
|
|
67
|
+
const hasStoragePathCallback = typeof storagePathValue === "function";
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<>
|
|
71
|
+
|
|
72
|
+
<div className={"col-span-12"}>
|
|
73
|
+
|
|
74
|
+
<ExpandablePanel
|
|
75
|
+
title={
|
|
76
|
+
<div className="flex flex-row text-gray-500">
|
|
77
|
+
<FileUploadIcon/>
|
|
78
|
+
<Typography variant={"subtitle2"}
|
|
79
|
+
className="ml-2">
|
|
80
|
+
File upload config
|
|
81
|
+
</Typography>
|
|
82
|
+
</div>
|
|
83
|
+
}>
|
|
84
|
+
|
|
85
|
+
<div className={"grid grid-cols-12 gap-2 p-4"}>
|
|
86
|
+
|
|
87
|
+
<div className={"col-span-12"}>
|
|
88
|
+
|
|
89
|
+
<MultiSelect
|
|
90
|
+
disabled={disabled}
|
|
91
|
+
name={acceptedFiles}
|
|
92
|
+
value={fileTypesValue ?? []}
|
|
93
|
+
onMultiValueChange={handleTypesChange}
|
|
94
|
+
label={allFileTypesSelected ? undefined : "Allowed file types"}
|
|
95
|
+
renderValues={(selected) => {
|
|
96
|
+
if (!selected || selected.length === 0) return "All file types allowed";
|
|
97
|
+
return selected.map((v: string) => fileTypes[v])
|
|
98
|
+
.filter((v: string) => Boolean(v))
|
|
99
|
+
.join(", ");
|
|
100
|
+
}}>
|
|
101
|
+
|
|
102
|
+
<MultiSelectItem key={"all"} value={"all"} className={"flex items-center gap-2"}>
|
|
103
|
+
<Checkbox
|
|
104
|
+
checked={!fileTypesValue}/>
|
|
105
|
+
All
|
|
106
|
+
</MultiSelectItem>
|
|
107
|
+
|
|
108
|
+
{Object.entries(fileTypes).map(([value, label]) => (
|
|
109
|
+
<MultiSelectItem key={value} value={value} className={"flex items-center gap-2"}>
|
|
110
|
+
<Checkbox
|
|
111
|
+
checked={allFileTypesSelected || fileTypesValue.indexOf(value) > -1}/>
|
|
112
|
+
<div className={"flex-grow"}>
|
|
113
|
+
{label}
|
|
114
|
+
</div>
|
|
115
|
+
<Button size={"small"}
|
|
116
|
+
variant={"outlined"}
|
|
117
|
+
onClick={(e) => {
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
e.stopPropagation();
|
|
120
|
+
return setFieldValue(acceptedFiles, [value]);
|
|
121
|
+
}}>
|
|
122
|
+
Only
|
|
123
|
+
</Button>
|
|
124
|
+
</MultiSelectItem>
|
|
125
|
+
))}
|
|
126
|
+
|
|
127
|
+
</MultiSelect>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div className={"col-span-12"}>
|
|
131
|
+
<Field name={fileName}
|
|
132
|
+
as={DebouncedTextField}
|
|
133
|
+
label={"File name"}
|
|
134
|
+
size={"small"}
|
|
135
|
+
disabled={hasFilenameCallback || disabled}
|
|
136
|
+
value={hasFilenameCallback ? "-" : fileNameValue}
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
<div className={"col-span-12"}>
|
|
140
|
+
<Field name={storagePath}
|
|
141
|
+
as={DebouncedTextField}
|
|
142
|
+
label={"Storage path"}
|
|
143
|
+
disabled={hasStoragePathCallback || disabled}
|
|
144
|
+
size={"small"}
|
|
145
|
+
value={hasStoragePathCallback ? "-" : storagePathValue}
|
|
146
|
+
/>
|
|
147
|
+
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
148
|
+
<p>You can use the following placeholders in
|
|
149
|
+
the file name
|
|
150
|
+
and storage path values:</p>
|
|
151
|
+
<ul>
|
|
152
|
+
<li>{"{file} - Full name of the uploaded file"}</li>
|
|
153
|
+
<li>{"{file.name} - Name of the uploaded file without extension"}</li>
|
|
154
|
+
<li>{"{file.ext} - Extension of the uploaded file"}</li>
|
|
155
|
+
<li>{"{entityId} - ID of the entity"}</li>
|
|
156
|
+
<li>{"{propertyKey} - ID of this field"}</li>
|
|
157
|
+
<li>{"{path} - Path of this entity"}</li>
|
|
158
|
+
<li>{"{rand} - Random value used to avoid name collisions"}</li>
|
|
159
|
+
</ul>
|
|
160
|
+
</Typography>
|
|
161
|
+
<Field type="checkbox"
|
|
162
|
+
name={storeUrl}
|
|
163
|
+
label={"Save URL instead of storage path"}
|
|
164
|
+
disabled={existing || disabled}
|
|
165
|
+
component={SwitchControl}/>
|
|
166
|
+
<br/>
|
|
167
|
+
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
168
|
+
Turn this setting on, if you prefer to save
|
|
169
|
+
the download
|
|
170
|
+
URL of the uploaded file instead of the
|
|
171
|
+
storage path.
|
|
172
|
+
You can only change this prop upon creation.
|
|
173
|
+
</Typography>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</ExpandablePanel>
|
|
177
|
+
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div className={"col-span-12"}>
|
|
181
|
+
|
|
182
|
+
<ValidationPanel>
|
|
183
|
+
{!multiple && <div className={"grid grid-cols-12 gap-2"}>
|
|
184
|
+
<GeneralPropertyValidation disabled={disabled}/>
|
|
185
|
+
</div>}
|
|
186
|
+
{multiple && <div className={"col-span-12"}>
|
|
187
|
+
<ArrayPropertyValidation disabled={disabled}/>
|
|
188
|
+
</div>}
|
|
189
|
+
</ValidationPanel>
|
|
190
|
+
|
|
191
|
+
</div>
|
|
192
|
+
</>
|
|
193
|
+
);
|
|
194
|
+
}
|