@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.
Files changed (90) hide show
  1. package/dist/ConfigControllerProvider.d.ts +1 -1
  2. package/dist/components/RootCollectionSuggestions.d.ts +1 -0
  3. package/dist/components/collection_editor/CollectionEditorDialog.d.ts +7 -3
  4. package/dist/components/collection_editor/CollectionPropertiesEditorForm.d.ts +5 -3
  5. package/dist/components/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
  6. package/dist/components/collection_editor/PropertyEditView.d.ts +5 -4
  7. package/dist/components/collection_editor/PropertyFieldPreview.d.ts +4 -3
  8. package/dist/components/collection_editor/PropertySelectItem.d.ts +2 -2
  9. package/dist/components/collection_editor/PropertyTree.d.ts +6 -5
  10. package/dist/components/collection_editor/import/CollectionEditorImportMapping.d.ts +3 -1
  11. package/dist/components/collection_editor/properties/BlockPropertyField.d.ts +3 -1
  12. package/dist/components/collection_editor/properties/MapPropertyField.d.ts +3 -1
  13. package/dist/components/collection_editor/properties/RepeatPropertyField.d.ts +3 -1
  14. package/dist/components/collection_editor/utils/update_property_for_widget.d.ts +2 -3
  15. package/dist/index.d.ts +0 -1
  16. package/dist/index.es.js +2221 -1889
  17. package/dist/index.es.js.map +1 -1
  18. package/dist/index.umd.js +1 -1
  19. package/dist/index.umd.js.map +1 -1
  20. package/dist/types/collection_editor_controller.d.ts +6 -1
  21. package/dist/types/config_controller.d.ts +2 -2
  22. package/dist/types/persisted_collection.d.ts +3 -2
  23. package/dist/utils/entities.d.ts +3 -4
  24. package/dist/utils/icons.d.ts +1 -2
  25. package/dist/utils/join_collections.d.ts +14 -0
  26. package/package.json +10 -5
  27. package/src/ConfigControllerProvider.tsx +177 -0
  28. package/src/components/EditorCollectionAction.tsx +95 -0
  29. package/src/components/HomePageEditorCollectionAction.tsx +81 -0
  30. package/src/components/NewCollectionCard.tsx +45 -0
  31. package/src/components/RootCollectionSuggestions.tsx +53 -0
  32. package/src/components/collection_editor/CollectionDetailsForm.tsx +312 -0
  33. package/src/components/collection_editor/CollectionEditorDialog.tsx +640 -0
  34. package/src/components/collection_editor/CollectionEditorWelcomeView.tsx +212 -0
  35. package/src/components/collection_editor/CollectionPropertiesEditorForm.tsx +450 -0
  36. package/src/components/collection_editor/CollectionYupValidation.tsx +6 -0
  37. package/src/components/collection_editor/EntityCustomViewsSelectDialog.tsx +29 -0
  38. package/src/components/collection_editor/EnumForm.tsx +354 -0
  39. package/src/components/collection_editor/PropertyEditView.tsx +535 -0
  40. package/src/components/collection_editor/PropertyFieldPreview.tsx +205 -0
  41. package/src/components/collection_editor/PropertySelectItem.tsx +31 -0
  42. package/src/components/collection_editor/PropertyTree.tsx +228 -0
  43. package/src/components/collection_editor/SelectIcons.tsx +72 -0
  44. package/src/components/collection_editor/SubcollectionsEditTab.tsx +239 -0
  45. package/src/components/collection_editor/UnsavedChangesDialog.tsx +47 -0
  46. package/src/components/collection_editor/import/CollectionEditorImportDataPreview.tsx +37 -0
  47. package/src/components/collection_editor/import/CollectionEditorImportMapping.tsx +236 -0
  48. package/src/components/collection_editor/import/clean_import_data.ts +53 -0
  49. package/src/components/collection_editor/properties/BlockPropertyField.tsx +131 -0
  50. package/src/components/collection_editor/properties/BooleanPropertyField.tsx +36 -0
  51. package/src/components/collection_editor/properties/CommonPropertyFields.tsx +112 -0
  52. package/src/components/collection_editor/properties/DateTimePropertyField.tsx +86 -0
  53. package/src/components/collection_editor/properties/EnumPropertyField.tsx +116 -0
  54. package/src/components/collection_editor/properties/FieldHelperView.tsx +13 -0
  55. package/src/components/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
  56. package/src/components/collection_editor/properties/MapPropertyField.tsx +154 -0
  57. package/src/components/collection_editor/properties/NumberPropertyField.tsx +38 -0
  58. package/src/components/collection_editor/properties/ReferencePropertyField.tsx +184 -0
  59. package/src/components/collection_editor/properties/RepeatPropertyField.tsx +115 -0
  60. package/src/components/collection_editor/properties/StoragePropertyField.tsx +194 -0
  61. package/src/components/collection_editor/properties/StringPropertyField.tsx +85 -0
  62. package/src/components/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +36 -0
  63. package/src/components/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
  64. package/src/components/collection_editor/properties/validation/GeneralPropertyValidation.tsx +49 -0
  65. package/src/components/collection_editor/properties/validation/NumberPropertyValidation.tsx +99 -0
  66. package/src/components/collection_editor/properties/validation/StringPropertyValidation.tsx +131 -0
  67. package/src/components/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
  68. package/src/components/collection_editor/templates/blog_template.ts +115 -0
  69. package/src/components/collection_editor/templates/products_template.ts +89 -0
  70. package/src/components/collection_editor/templates/users_template.ts +34 -0
  71. package/src/components/collection_editor/util.ts +21 -0
  72. package/src/components/collection_editor/utils/supported_fields.tsx +28 -0
  73. package/src/components/collection_editor/utils/update_property_for_widget.ts +258 -0
  74. package/src/components/collection_editor/utils/useTraceUpdate.tsx +23 -0
  75. package/src/index.ts +31 -0
  76. package/src/types/collection_editor_controller.tsx +31 -0
  77. package/src/types/collection_inference.ts +3 -0
  78. package/src/types/config_controller.tsx +30 -0
  79. package/src/types/config_permissions.ts +20 -0
  80. package/src/types/persisted_collection.ts +7 -0
  81. package/src/useCollectionEditorController.tsx +9 -0
  82. package/src/useCollectionEditorPlugin.tsx +103 -0
  83. package/src/useCollectionsConfigController.tsx +9 -0
  84. package/src/utils/arrays.ts +3 -0
  85. package/src/utils/entities.ts +38 -0
  86. package/src/utils/icons.ts +17 -0
  87. package/src/utils/join_collections.ts +144 -0
  88. package/src/utils/synonyms.ts +1952 -0
  89. package/src/vite-env.d.ts +1 -0
  90. 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
+ }