@firecms/collection_editor 3.0.0-alpha.16 → 3.0.0-alpha.18
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 +1 -1
- package/dist/components/RootCollectionSuggestions.d.ts +1 -0
- package/dist/components/collection_editor/CollectionEditorDialog.d.ts +7 -3
- package/dist/components/collection_editor/CollectionPropertiesEditorForm.d.ts +5 -3
- package/dist/components/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
- package/dist/components/collection_editor/PropertyEditView.d.ts +5 -4
- package/dist/components/collection_editor/PropertyFieldPreview.d.ts +4 -3
- package/dist/components/collection_editor/PropertySelectItem.d.ts +2 -2
- package/dist/components/collection_editor/PropertyTree.d.ts +6 -5
- package/dist/components/collection_editor/import/CollectionEditorImportMapping.d.ts +3 -1
- package/dist/components/collection_editor/properties/BlockPropertyField.d.ts +3 -1
- package/dist/components/collection_editor/properties/MapPropertyField.d.ts +3 -1
- package/dist/components/collection_editor/properties/RepeatPropertyField.d.ts +3 -1
- package/dist/components/collection_editor/utils/update_property_for_widget.d.ts +2 -3
- package/dist/index.d.ts +0 -1
- package/dist/index.es.js +2221 -1889
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +6 -1
- package/dist/types/config_controller.d.ts +2 -2
- package/dist/types/persisted_collection.d.ts +3 -2
- package/dist/utils/entities.d.ts +3 -4
- package/dist/utils/icons.d.ts +1 -2
- package/dist/utils/join_collections.d.ts +14 -0
- package/package.json +10 -5
- package/src/ConfigControllerProvider.tsx +177 -0
- package/src/components/EditorCollectionAction.tsx +95 -0
- package/src/components/HomePageEditorCollectionAction.tsx +81 -0
- package/src/components/NewCollectionCard.tsx +45 -0
- package/src/components/RootCollectionSuggestions.tsx +53 -0
- package/src/components/collection_editor/CollectionDetailsForm.tsx +312 -0
- package/src/components/collection_editor/CollectionEditorDialog.tsx +640 -0
- package/src/components/collection_editor/CollectionEditorWelcomeView.tsx +212 -0
- package/src/components/collection_editor/CollectionPropertiesEditorForm.tsx +450 -0
- package/src/components/collection_editor/CollectionYupValidation.tsx +6 -0
- package/src/components/collection_editor/EntityCustomViewsSelectDialog.tsx +29 -0
- package/src/components/collection_editor/EnumForm.tsx +354 -0
- package/src/components/collection_editor/PropertyEditView.tsx +535 -0
- package/src/components/collection_editor/PropertyFieldPreview.tsx +205 -0
- package/src/components/collection_editor/PropertySelectItem.tsx +31 -0
- package/src/components/collection_editor/PropertyTree.tsx +228 -0
- package/src/components/collection_editor/SelectIcons.tsx +72 -0
- package/src/components/collection_editor/SubcollectionsEditTab.tsx +239 -0
- package/src/components/collection_editor/UnsavedChangesDialog.tsx +47 -0
- package/src/components/collection_editor/import/CollectionEditorImportDataPreview.tsx +37 -0
- package/src/components/collection_editor/import/CollectionEditorImportMapping.tsx +236 -0
- package/src/components/collection_editor/import/clean_import_data.ts +53 -0
- package/src/components/collection_editor/properties/BlockPropertyField.tsx +131 -0
- package/src/components/collection_editor/properties/BooleanPropertyField.tsx +36 -0
- package/src/components/collection_editor/properties/CommonPropertyFields.tsx +112 -0
- package/src/components/collection_editor/properties/DateTimePropertyField.tsx +86 -0
- package/src/components/collection_editor/properties/EnumPropertyField.tsx +116 -0
- package/src/components/collection_editor/properties/FieldHelperView.tsx +13 -0
- package/src/components/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
- package/src/components/collection_editor/properties/MapPropertyField.tsx +154 -0
- package/src/components/collection_editor/properties/NumberPropertyField.tsx +38 -0
- package/src/components/collection_editor/properties/ReferencePropertyField.tsx +184 -0
- package/src/components/collection_editor/properties/RepeatPropertyField.tsx +115 -0
- package/src/components/collection_editor/properties/StoragePropertyField.tsx +194 -0
- package/src/components/collection_editor/properties/StringPropertyField.tsx +85 -0
- package/src/components/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +36 -0
- package/src/components/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
- package/src/components/collection_editor/properties/validation/GeneralPropertyValidation.tsx +49 -0
- package/src/components/collection_editor/properties/validation/NumberPropertyValidation.tsx +99 -0
- package/src/components/collection_editor/properties/validation/StringPropertyValidation.tsx +131 -0
- package/src/components/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
- package/src/components/collection_editor/templates/blog_template.ts +115 -0
- package/src/components/collection_editor/templates/products_template.ts +89 -0
- package/src/components/collection_editor/templates/users_template.ts +34 -0
- package/src/components/collection_editor/util.ts +21 -0
- package/src/components/collection_editor/utils/supported_fields.tsx +28 -0
- package/src/components/collection_editor/utils/update_property_for_widget.ts +258 -0
- package/src/components/collection_editor/utils/useTraceUpdate.tsx +23 -0
- package/src/index.ts +31 -0
- package/src/types/collection_editor_controller.tsx +31 -0
- package/src/types/collection_inference.ts +3 -0
- package/src/types/config_controller.tsx +30 -0
- package/src/types/config_permissions.ts +20 -0
- package/src/types/persisted_collection.ts +7 -0
- package/src/useCollectionEditorController.tsx +9 -0
- package/src/useCollectionEditorPlugin.tsx +103 -0
- package/src/useCollectionsConfigController.tsx +9 -0
- package/src/utils/arrays.ts +3 -0
- package/src/utils/entities.ts +38 -0
- package/src/utils/icons.ts +17 -0
- package/src/utils/join_collections.ts +144 -0
- package/src/utils/synonyms.ts +1952 -0
- package/src/vite-env.d.ts +1 -0
- package/dist/types/editable_properties.d.ts +0 -10
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Autocomplete,
|
|
4
|
+
AutocompleteItem,
|
|
5
|
+
BooleanSwitchWithLabel,
|
|
6
|
+
Chip,
|
|
7
|
+
cn,
|
|
8
|
+
Container,
|
|
9
|
+
DebouncedTextField,
|
|
10
|
+
Dialog,
|
|
11
|
+
EntityCollection,
|
|
12
|
+
ExpandablePanel,
|
|
13
|
+
getIconForView,
|
|
14
|
+
IconButton,
|
|
15
|
+
Select,
|
|
16
|
+
SelectItem,
|
|
17
|
+
SettingsIcon,
|
|
18
|
+
singular,
|
|
19
|
+
TextField,
|
|
20
|
+
Tooltip,
|
|
21
|
+
toSnakeCase,
|
|
22
|
+
Typography,
|
|
23
|
+
useAutoComplete
|
|
24
|
+
} from "@firecms/core";
|
|
25
|
+
import { Field, getIn, useFormikContext } from "formik";
|
|
26
|
+
|
|
27
|
+
//@ts-ignore
|
|
28
|
+
import { SearchIcons } from "./SelectIcons";
|
|
29
|
+
import { FieldHelperView } from "./properties/FieldHelperView";
|
|
30
|
+
|
|
31
|
+
export function CollectionDetailsForm({
|
|
32
|
+
isNewCollection,
|
|
33
|
+
reservedGroups,
|
|
34
|
+
existingPaths,
|
|
35
|
+
existingAliases,
|
|
36
|
+
groups,
|
|
37
|
+
parentCollection
|
|
38
|
+
}: {
|
|
39
|
+
isNewCollection: boolean,
|
|
40
|
+
reservedGroups?: string[];
|
|
41
|
+
existingPaths?: string[];
|
|
42
|
+
existingAliases?: string[];
|
|
43
|
+
groups: string[] | null;
|
|
44
|
+
parentCollection?: EntityCollection;
|
|
45
|
+
}) {
|
|
46
|
+
|
|
47
|
+
const groupRef = React.useRef<HTMLInputElement>(null);
|
|
48
|
+
const {
|
|
49
|
+
values,
|
|
50
|
+
setFieldValue,
|
|
51
|
+
handleChange,
|
|
52
|
+
touched,
|
|
53
|
+
errors,
|
|
54
|
+
setFieldTouched,
|
|
55
|
+
isSubmitting,
|
|
56
|
+
submitCount
|
|
57
|
+
} = useFormikContext<EntityCollection>();
|
|
58
|
+
|
|
59
|
+
const [iconDialogOpen, setIconDialogOpen] = useState(false);
|
|
60
|
+
|
|
61
|
+
const updateName = (name: string) => {
|
|
62
|
+
setFieldValue("name", name);
|
|
63
|
+
|
|
64
|
+
const pathTouched = getIn(touched, "path");
|
|
65
|
+
if (!pathTouched && isNewCollection && name) {
|
|
66
|
+
setFieldValue("path", toSnakeCase(name));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const singularNameTouched = getIn(touched, "singularName");
|
|
70
|
+
if (!singularNameTouched && isNewCollection && name) {
|
|
71
|
+
setFieldValue("singularName", singular(name))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const collectionIcon = getIconForView(values);
|
|
77
|
+
|
|
78
|
+
const validatePath = useCallback((value: string) => {
|
|
79
|
+
let error;
|
|
80
|
+
if (!value) {
|
|
81
|
+
error = "You must specify a path in the database for this collection";
|
|
82
|
+
}
|
|
83
|
+
if (isNewCollection && existingAliases?.includes(value.trim().toLowerCase()))
|
|
84
|
+
error = "There is already a collection which uses this path as an alias";
|
|
85
|
+
if (isNewCollection && existingPaths?.includes(value.trim().toLowerCase()) && !values.alias)
|
|
86
|
+
error = "There is already a collection with the specified path. If you want to have multiple collections referring to the same database path, you need to define an alias in at least one of the collections";
|
|
87
|
+
return error;
|
|
88
|
+
}, [isNewCollection, existingAliases, existingPaths, values.alias]);
|
|
89
|
+
|
|
90
|
+
const validateAlias = useCallback((value: string) => {
|
|
91
|
+
if (!value) return undefined;
|
|
92
|
+
let error;
|
|
93
|
+
if (isNewCollection && existingPaths?.includes(value.trim().toLowerCase()))
|
|
94
|
+
error = "There is already a collection that uses this value as a path";
|
|
95
|
+
if (isNewCollection && existingAliases?.includes(value.trim().toLowerCase()))
|
|
96
|
+
error = "There is already a collection which uses this alias";
|
|
97
|
+
return error;
|
|
98
|
+
}, [isNewCollection, existingPaths, existingAliases]);
|
|
99
|
+
|
|
100
|
+
const groupOptions = groups?.filter((group) => !reservedGroups?.includes(group));
|
|
101
|
+
|
|
102
|
+
const {
|
|
103
|
+
inputFocused,
|
|
104
|
+
autoCompleteOpen,
|
|
105
|
+
setAutoCompleteOpen
|
|
106
|
+
} = useAutoComplete({
|
|
107
|
+
ref: groupRef
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const isSubcollection = !!parentCollection;
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div className={"overflow-auto my-auto"}>
|
|
114
|
+
<Container maxWidth={"4xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
|
|
115
|
+
|
|
116
|
+
<div>
|
|
117
|
+
<div
|
|
118
|
+
className="flex flex-row py-2 pt-3 items-center">
|
|
119
|
+
<Typography variant={!isNewCollection ? "h5" : "h4"} className={"flex-grow"}>
|
|
120
|
+
{isNewCollection ? "New collection" : `${values.name} collection`}
|
|
121
|
+
</Typography>
|
|
122
|
+
<Tooltip title={"Change icon"}>
|
|
123
|
+
<IconButton
|
|
124
|
+
shape={"square"}
|
|
125
|
+
onClick={() => setIconDialogOpen(true)}>
|
|
126
|
+
{collectionIcon}
|
|
127
|
+
</IconButton>
|
|
128
|
+
</Tooltip>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{parentCollection && <Chip colorScheme={"tealDarker"}>
|
|
132
|
+
<Typography variant={"caption"}>
|
|
133
|
+
This is a subcollection of <b>{parentCollection.name}</b>
|
|
134
|
+
</Typography>
|
|
135
|
+
</Chip>}
|
|
136
|
+
|
|
137
|
+
</div>
|
|
138
|
+
<div className={"grid grid-cols-12 gap-4"}>
|
|
139
|
+
|
|
140
|
+
<div className={"col-span-12"}>
|
|
141
|
+
<TextField
|
|
142
|
+
value={values.name ?? ""}
|
|
143
|
+
onChange={(e: any) => updateName(e.target.value)}
|
|
144
|
+
label={"Name"}
|
|
145
|
+
required
|
|
146
|
+
error={touched.name && Boolean(errors.name)}/>
|
|
147
|
+
<FieldHelperView error={touched.name && Boolean(errors.name)}>
|
|
148
|
+
{touched.name && Boolean(errors.name) ? errors.name : "Name of in this collection, usually a plural name (e.g. Products)"}
|
|
149
|
+
</FieldHelperView>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<div className={cn("col-span-12 ", isSubcollection ? "" : "sm:col-span-8")}>
|
|
153
|
+
<Field name={"path"}
|
|
154
|
+
as={DebouncedTextField}
|
|
155
|
+
label={"Path"}
|
|
156
|
+
validate={validatePath}
|
|
157
|
+
disabled={!isNewCollection}
|
|
158
|
+
required
|
|
159
|
+
error={touched.path && Boolean(errors.path)}/>
|
|
160
|
+
|
|
161
|
+
<FieldHelperView error={touched.path && Boolean(errors.path)}>
|
|
162
|
+
{touched.path && Boolean(errors.path) ? errors.path : "Path that this collection is stored in"}
|
|
163
|
+
</FieldHelperView>
|
|
164
|
+
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
{!isSubcollection && <div className={"col-span-12 sm:col-span-4 relative"}>
|
|
168
|
+
|
|
169
|
+
<TextField error={touched.group && Boolean(errors.group)}
|
|
170
|
+
disabled={isSubmitting}
|
|
171
|
+
value={values.group ?? ""}
|
|
172
|
+
autoComplete="off"
|
|
173
|
+
onChange={(event) => setFieldValue("group", event.target.value)}
|
|
174
|
+
name={"group"}
|
|
175
|
+
inputRef={groupRef}
|
|
176
|
+
label="Group"/>
|
|
177
|
+
<Autocomplete
|
|
178
|
+
open={autoCompleteOpen && (groupOptions ?? []).length > 0}
|
|
179
|
+
setOpen={setAutoCompleteOpen}>
|
|
180
|
+
{groupOptions?.map((group, index) => {
|
|
181
|
+
return <AutocompleteItem
|
|
182
|
+
key={index + "_" + group}
|
|
183
|
+
onClick={() => {
|
|
184
|
+
setAutoCompleteOpen(false);
|
|
185
|
+
setFieldValue("group", group ?? null);
|
|
186
|
+
}}
|
|
187
|
+
>
|
|
188
|
+
<div className={"flex-grow"}>
|
|
189
|
+
{group}
|
|
190
|
+
</div>
|
|
191
|
+
</AutocompleteItem>;
|
|
192
|
+
})}
|
|
193
|
+
</Autocomplete>
|
|
194
|
+
<FieldHelperView>
|
|
195
|
+
{touched.group && Boolean(errors.group) ? errors.group : "Group of the collection"}
|
|
196
|
+
</FieldHelperView>
|
|
197
|
+
</div>}
|
|
198
|
+
|
|
199
|
+
<div className={"col-span-12"}>
|
|
200
|
+
<ExpandablePanel
|
|
201
|
+
title={
|
|
202
|
+
<div className="flex flex-row text-gray-500">
|
|
203
|
+
<SettingsIcon/>
|
|
204
|
+
<Typography variant={"subtitle2"}
|
|
205
|
+
className="ml-2">
|
|
206
|
+
Advanced
|
|
207
|
+
</Typography>
|
|
208
|
+
</div>}
|
|
209
|
+
initiallyExpanded={false}>
|
|
210
|
+
<div className={"grid grid-cols-12 gap-4 p-4"}>
|
|
211
|
+
<div className={"col-span-12"}>
|
|
212
|
+
<TextField
|
|
213
|
+
error={touched.singularName && Boolean(errors.singularName)}
|
|
214
|
+
id={"singularName"}
|
|
215
|
+
aria-describedby={"singularName-helper"}
|
|
216
|
+
onChange={(e) => {
|
|
217
|
+
setFieldTouched("singularName", true);
|
|
218
|
+
return handleChange(e);
|
|
219
|
+
}}
|
|
220
|
+
value={values.singularName ?? ""}
|
|
221
|
+
label={"Singular name"}/>
|
|
222
|
+
<FieldHelperView error={touched.singularName && Boolean(errors.singularName)}>
|
|
223
|
+
{touched.singularName && Boolean(errors.singularName) ? errors.singularName : "Optionally define a singular name for your entities"}
|
|
224
|
+
</FieldHelperView>
|
|
225
|
+
</div>
|
|
226
|
+
<div className={"col-span-12"}>
|
|
227
|
+
<TextField
|
|
228
|
+
error={touched.description && Boolean(errors.description)}
|
|
229
|
+
id="description"
|
|
230
|
+
value={values.description ?? ""}
|
|
231
|
+
onChange={handleChange}
|
|
232
|
+
multiline
|
|
233
|
+
rows={2}
|
|
234
|
+
aria-describedby="description-helper-text"
|
|
235
|
+
label="Description"
|
|
236
|
+
/>
|
|
237
|
+
<FieldHelperView error={touched.description && Boolean(errors.description)}>
|
|
238
|
+
{touched.description && Boolean(errors.description) ? errors.description : "Description of the collection, you can use markdown"}
|
|
239
|
+
</FieldHelperView>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<div className={"col-span-12"}>
|
|
243
|
+
<Field name={"alias"}
|
|
244
|
+
as={DebouncedTextField}
|
|
245
|
+
label={"Alias"}
|
|
246
|
+
validate={validateAlias}
|
|
247
|
+
error={touched.alias && Boolean(errors.alias)}/>
|
|
248
|
+
<FieldHelperView error={touched.alias && Boolean(errors.alias)}>
|
|
249
|
+
{touched.alias && Boolean(errors.alias) ? errors.alias : "Use an alias as an ID when you have multiple collections located in the same path"}
|
|
250
|
+
</FieldHelperView>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<div className={"col-span-12"}>
|
|
254
|
+
<Select
|
|
255
|
+
name="defaultSize"
|
|
256
|
+
label="Default row size"
|
|
257
|
+
position={"item-aligned"}
|
|
258
|
+
onChange={handleChange}
|
|
259
|
+
value={values.defaultSize ?? ""}
|
|
260
|
+
renderValue={(value: any) => value.toUpperCase()}
|
|
261
|
+
>
|
|
262
|
+
{["xs", "s", "m", "l", "xl"].map((value) => (
|
|
263
|
+
<SelectItem
|
|
264
|
+
key={`size-select-${value}`}
|
|
265
|
+
value={value}>
|
|
266
|
+
{value.toUpperCase()}
|
|
267
|
+
</SelectItem>
|
|
268
|
+
))}
|
|
269
|
+
</Select>
|
|
270
|
+
</div>
|
|
271
|
+
<div className={"col-span-12"}>
|
|
272
|
+
<BooleanSwitchWithLabel
|
|
273
|
+
position={"start"}
|
|
274
|
+
label="Collection group"
|
|
275
|
+
onValueChange={(v) => setFieldValue("collectionGroup", v)}
|
|
276
|
+
value={values.collectionGroup ?? false}
|
|
277
|
+
/>
|
|
278
|
+
<FieldHelperView>
|
|
279
|
+
A collection group consists of all collections with the same ID. This allows you
|
|
280
|
+
to query over multiple collections at once.
|
|
281
|
+
</FieldHelperView>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
</ExpandablePanel>
|
|
285
|
+
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<div style={{ height: "52px" }}/>
|
|
291
|
+
|
|
292
|
+
<Dialog
|
|
293
|
+
open={iconDialogOpen}
|
|
294
|
+
onOpenChange={setIconDialogOpen}
|
|
295
|
+
maxWidth={"xl"}
|
|
296
|
+
fullWidth
|
|
297
|
+
>
|
|
298
|
+
<div className={"p-4 overflow-auto min-h-[200px]"}>
|
|
299
|
+
<SearchIcons selectedIcon={values.icon}
|
|
300
|
+
onIconSelected={(icon: string) => {
|
|
301
|
+
setIconDialogOpen(false);
|
|
302
|
+
setFieldValue("icon", icon);
|
|
303
|
+
}}/>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
</Dialog>
|
|
307
|
+
|
|
308
|
+
</Container>
|
|
309
|
+
</div>
|
|
310
|
+
)
|
|
311
|
+
;
|
|
312
|
+
}
|