@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.
Files changed (145) hide show
  1. package/dist/ConfigControllerProvider.d.ts +3 -2
  2. package/dist/index.d.ts +3 -2
  3. package/dist/index.es.js +3128 -4094
  4. package/dist/index.es.js.map +1 -1
  5. package/dist/index.umd.js +3 -1
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/types/collection_editor_controller.d.ts +22 -7
  8. package/dist/types/collection_inference.d.ts +1 -1
  9. package/dist/types/config_controller.d.ts +32 -5
  10. package/dist/types/persisted_collection.d.ts +3 -1
  11. package/dist/ui/CollectionViewHeaderAction.d.ts +10 -0
  12. package/dist/{components → ui}/EditorCollectionAction.d.ts +1 -1
  13. package/dist/ui/MissingReferenceWidget.d.ts +3 -0
  14. package/dist/ui/NewCollectionButton.d.ts +1 -0
  15. package/dist/ui/PropertyAddColumnComponent.d.ts +6 -0
  16. package/dist/ui/RootCollectionSuggestions.d.ts +1 -0
  17. package/dist/{components → ui}/collection_editor/CollectionDetailsForm.d.ts +3 -2
  18. package/dist/{components → ui}/collection_editor/CollectionEditorDialog.d.ts +11 -6
  19. package/dist/{components → ui}/collection_editor/CollectionPropertiesEditorForm.d.ts +6 -3
  20. package/dist/{components → ui}/collection_editor/CollectionYupValidation.d.ts +3 -0
  21. package/dist/ui/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
  22. package/dist/ui/collection_editor/GetCodeDialog.d.ts +5 -0
  23. package/dist/{components → ui}/collection_editor/PropertyEditView.d.ts +8 -6
  24. package/dist/{components → ui}/collection_editor/PropertyFieldPreview.d.ts +4 -3
  25. package/dist/ui/collection_editor/PropertySelectItem.d.ts +8 -0
  26. package/dist/{components → ui}/collection_editor/PropertyTree.d.ts +8 -5
  27. package/dist/{components → ui}/collection_editor/SubcollectionsEditTab.d.ts +2 -2
  28. package/dist/{components → ui}/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
  29. package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +7 -0
  30. package/dist/{components → ui}/collection_editor/import/clean_import_data.d.ts +1 -1
  31. package/dist/{components → ui}/collection_editor/properties/BlockPropertyField.d.ts +4 -1
  32. package/dist/{components → ui}/collection_editor/properties/CommonPropertyFields.d.ts +1 -0
  33. package/dist/{components → ui}/collection_editor/properties/MapPropertyField.d.ts +4 -1
  34. package/dist/{components → ui}/collection_editor/properties/RepeatPropertyField.d.ts +4 -1
  35. package/dist/{components → ui}/collection_editor/properties/StringPropertyField.d.ts +1 -1
  36. package/dist/ui/collection_editor/properties/UrlPropertyField.d.ts +4 -0
  37. package/dist/ui/collection_editor/templates/blog_template.d.ts +2 -0
  38. package/dist/ui/collection_editor/templates/pages_template.d.ts +2 -0
  39. package/dist/ui/collection_editor/templates/products_template.d.ts +2 -0
  40. package/dist/ui/collection_editor/templates/users_template.d.ts +2 -0
  41. package/dist/ui/collection_editor/utils/strings.d.ts +1 -0
  42. package/dist/ui/collection_editor/utils/supported_fields.d.ts +3 -0
  43. package/dist/ui/collection_editor/utils/update_property_for_widget.d.ts +2 -0
  44. package/dist/useCollectionEditorPlugin.d.ts +5 -3
  45. package/dist/utils/entities.d.ts +3 -4
  46. package/package.json +22 -19
  47. package/src/ConfigControllerProvider.tsx +336 -0
  48. package/src/index.ts +35 -0
  49. package/src/types/collection_editor_controller.tsx +42 -0
  50. package/src/types/collection_inference.ts +3 -0
  51. package/src/types/config_controller.tsx +60 -0
  52. package/src/types/config_permissions.ts +20 -0
  53. package/src/types/persisted_collection.ts +9 -0
  54. package/src/ui/CollectionViewHeaderAction.tsx +43 -0
  55. package/src/ui/EditorCollectionAction.tsx +109 -0
  56. package/src/ui/HomePageEditorCollectionAction.tsx +84 -0
  57. package/src/ui/MissingReferenceWidget.tsx +35 -0
  58. package/src/ui/NewCollectionButton.tsx +16 -0
  59. package/src/ui/NewCollectionCard.tsx +47 -0
  60. package/src/ui/PropertyAddColumnComponent.tsx +42 -0
  61. package/src/ui/RootCollectionSuggestions.tsx +55 -0
  62. package/src/ui/collection_editor/CollectionDetailsForm.tsx +366 -0
  63. package/src/ui/collection_editor/CollectionEditorDialog.tsx +754 -0
  64. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +206 -0
  65. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +481 -0
  66. package/src/ui/collection_editor/CollectionYupValidation.tsx +7 -0
  67. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +37 -0
  68. package/src/ui/collection_editor/EnumForm.tsx +354 -0
  69. package/src/ui/collection_editor/GetCodeDialog.tsx +110 -0
  70. package/src/ui/collection_editor/PropertyEditView.tsx +558 -0
  71. package/src/ui/collection_editor/PropertyFieldPreview.tsx +203 -0
  72. package/src/ui/collection_editor/PropertySelectItem.tsx +32 -0
  73. package/src/ui/collection_editor/PropertyTree.tsx +233 -0
  74. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +253 -0
  75. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +47 -0
  76. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +37 -0
  77. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +260 -0
  78. package/src/ui/collection_editor/import/clean_import_data.ts +53 -0
  79. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +135 -0
  80. package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +36 -0
  81. package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +137 -0
  82. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +87 -0
  83. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +117 -0
  84. package/src/ui/collection_editor/properties/FieldHelperView.tsx +13 -0
  85. package/src/ui/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
  86. package/src/ui/collection_editor/properties/MapPropertyField.tsx +149 -0
  87. package/src/ui/collection_editor/properties/NumberPropertyField.tsx +38 -0
  88. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +165 -0
  89. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +108 -0
  90. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +194 -0
  91. package/src/ui/collection_editor/properties/StringPropertyField.tsx +79 -0
  92. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +89 -0
  93. package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +36 -0
  94. package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
  95. package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +50 -0
  96. package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +100 -0
  97. package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +132 -0
  98. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
  99. package/src/ui/collection_editor/templates/blog_template.ts +115 -0
  100. package/src/ui/collection_editor/templates/pages_template.ts +188 -0
  101. package/src/ui/collection_editor/templates/products_template.ts +88 -0
  102. package/src/ui/collection_editor/templates/users_template.ts +42 -0
  103. package/src/ui/collection_editor/util.ts +21 -0
  104. package/src/ui/collection_editor/utils/strings.ts +8 -0
  105. package/src/ui/collection_editor/utils/supported_fields.tsx +29 -0
  106. package/src/ui/collection_editor/utils/update_property_for_widget.ts +271 -0
  107. package/src/ui/collection_editor/utils/useTraceUpdate.tsx +23 -0
  108. package/src/useCollectionEditorController.tsx +9 -0
  109. package/src/useCollectionEditorPlugin.tsx +137 -0
  110. package/src/useCollectionsConfigController.tsx +9 -0
  111. package/src/utils/arrays.ts +3 -0
  112. package/src/utils/entities.ts +38 -0
  113. package/src/vite-env.d.ts +1 -0
  114. package/dist/components/collection_editor/PropertySelectItem.d.ts +0 -8
  115. package/dist/components/collection_editor/SelectIcons.d.ts +0 -6
  116. package/dist/components/collection_editor/import/CollectionEditorImportMapping.d.ts +0 -4
  117. package/dist/components/collection_editor/templates/blog_template.d.ts +0 -10
  118. package/dist/components/collection_editor/templates/products_template.d.ts +0 -12
  119. package/dist/components/collection_editor/templates/users_template.d.ts +0 -7
  120. package/dist/components/collection_editor/utils/supported_fields.d.ts +0 -3
  121. package/dist/components/collection_editor/utils/update_property_for_widget.d.ts +0 -3
  122. package/dist/types/editable_properties.d.ts +0 -10
  123. package/dist/utils/icons.d.ts +0 -2
  124. package/dist/utils/synonyms.d.ts +0 -1951
  125. /package/dist/{components → ui}/HomePageEditorCollectionAction.d.ts +0 -0
  126. /package/dist/{components → ui}/NewCollectionCard.d.ts +0 -0
  127. /package/dist/{components → ui}/collection_editor/CollectionEditorWelcomeView.d.ts +0 -0
  128. /package/dist/{components → ui}/collection_editor/EnumForm.d.ts +0 -0
  129. /package/dist/{components → ui}/collection_editor/UnsavedChangesDialog.d.ts +0 -0
  130. /package/dist/{components → ui}/collection_editor/properties/BooleanPropertyField.d.ts +0 -0
  131. /package/dist/{components → ui}/collection_editor/properties/DateTimePropertyField.d.ts +0 -0
  132. /package/dist/{components → ui}/collection_editor/properties/EnumPropertyField.d.ts +0 -0
  133. /package/dist/{components → ui}/collection_editor/properties/FieldHelperView.d.ts +0 -0
  134. /package/dist/{components → ui}/collection_editor/properties/KeyValuePropertyField.d.ts +0 -0
  135. /package/dist/{components → ui}/collection_editor/properties/NumberPropertyField.d.ts +0 -0
  136. /package/dist/{components → ui}/collection_editor/properties/ReferencePropertyField.d.ts +0 -0
  137. /package/dist/{components → ui}/collection_editor/properties/StoragePropertyField.d.ts +0 -0
  138. /package/dist/{components → ui}/collection_editor/properties/advanced/AdvancedPropertyValidation.d.ts +0 -0
  139. /package/dist/{components → ui}/collection_editor/properties/validation/ArrayPropertyValidation.d.ts +0 -0
  140. /package/dist/{components → ui}/collection_editor/properties/validation/GeneralPropertyValidation.d.ts +0 -0
  141. /package/dist/{components → ui}/collection_editor/properties/validation/NumberPropertyValidation.d.ts +0 -0
  142. /package/dist/{components → ui}/collection_editor/properties/validation/StringPropertyValidation.d.ts +0 -0
  143. /package/dist/{components → ui}/collection_editor/properties/validation/ValidationPanel.d.ts +0 -0
  144. /package/dist/{components → ui}/collection_editor/util.d.ts +0 -0
  145. /package/dist/{components → ui}/collection_editor/utils/useTraceUpdate.d.ts +0 -0
@@ -0,0 +1,7 @@
1
+ import * as Yup from "yup";
2
+
3
+ export const YupSchema = Yup.object().shape({
4
+ id: Yup.string().required("Required"),
5
+ name: Yup.string().required("Required"),
6
+ path: Yup.string().required("Required")
7
+ });
@@ -0,0 +1,37 @@
1
+ import { useCustomizationController } from "@firecms/core";
2
+ import { Button, Dialog, DialogActions, DialogContent, Typography } from "@firecms/ui";
3
+ import React from "react";
4
+
5
+ export function EntityCustomViewsSelectDialog({ open, onClose }: { open: boolean, onClose: (selectedViewKey?: string) => void }) {
6
+ const {
7
+ entityViews,
8
+ } = useCustomizationController();
9
+
10
+ return <Dialog
11
+ maxWidth={"md"}
12
+ open={open}>
13
+ <DialogContent className={"flex flex-col gap-4"}>
14
+ <Typography variant={"h6"}>
15
+ Select view
16
+ </Typography>
17
+ {entityViews?.map((view) => {
18
+ return <Button
19
+ key={view.key}
20
+ onClick={() => onClose(view.key)}
21
+ fullWidth
22
+ variant={"text"}
23
+ >
24
+ {view.name} ({view.key})
25
+ </Button>;
26
+ })}
27
+ {(entityViews ?? []).length === 0 &&
28
+ <Typography variant={"body2"}>
29
+ No custom views defined
30
+ </Typography>
31
+ }
32
+ </DialogContent>
33
+ <DialogActions>
34
+ <Button variant={"outlined"} onClick={() => onClose()}>Cancel</Button>
35
+ </DialogActions>
36
+ </Dialog>
37
+ }
@@ -0,0 +1,354 @@
1
+ import React, { useEffect } from "react";
2
+
3
+ import { FastField, Formik, getIn, useFormikContext } from "formik";
4
+ import { EnumValueConfig, EnumValues, FormikArrayContainer, } from "@firecms/core";
5
+ import {
6
+ AutoAwesomeIcon,
7
+ Button,
8
+ CircularProgress,
9
+ DebouncedTextField,
10
+ Dialog,
11
+ DialogActions,
12
+ DialogContent,
13
+ IconButton,
14
+ ListIcon,
15
+ Paper,
16
+ SettingsIcon,
17
+ Typography
18
+ } from "@firecms/ui";
19
+ import { FieldHelperView } from "./properties/FieldHelperView";
20
+ import { extractEnumFromValues } from "@firecms/schema_inference";
21
+
22
+ type EnumFormProps = {
23
+ enumValues: EnumValueConfig[];
24
+ onValuesChanged?: (enumValues: EnumValueConfig[]) => void;
25
+ onError?: (error: boolean) => void;
26
+ updateIds: boolean;
27
+ disabled: boolean;
28
+ allowDataInference?: boolean;
29
+ getData?: () => Promise<string[]>;
30
+ };
31
+ export const EnumForm = React.memo(
32
+ function EnumForm({
33
+ enumValues,
34
+ onValuesChanged,
35
+ onError,
36
+ updateIds,
37
+ disabled,
38
+ allowDataInference,
39
+ getData
40
+ }: EnumFormProps) {
41
+
42
+ return (
43
+ <Formik initialValues={{ enumValues }}
44
+ // enableReinitialize={true}
45
+ validateOnMount={true}
46
+ onSubmit={(data: { enumValues: EnumValueConfig[] }, formikHelpers) => {
47
+ }}
48
+ >
49
+ {
50
+ ({
51
+ values,
52
+ errors
53
+ }) => {
54
+ // eslint-disable-next-line react-hooks/rules-of-hooks
55
+ useEffect(() => {
56
+ if (onValuesChanged) {
57
+ onValuesChanged(values.enumValues);
58
+ }
59
+ }, [values.enumValues]);
60
+ // eslint-disable-next-line react-hooks/rules-of-hooks
61
+ useEffect(() => {
62
+ if (onError)
63
+ onError(Boolean(errors?.enumValues ?? false));
64
+ }, [errors]);
65
+
66
+ return <EnumFormFields enumValuesPath={"enumValues"}
67
+ values={values}
68
+ errors={errors}
69
+ shouldUpdateId={updateIds}
70
+ disabled={disabled}
71
+ allowDataInference={allowDataInference}
72
+ getData={getData}/>
73
+ }
74
+ }
75
+ </Formik>
76
+
77
+ );
78
+ },
79
+ function areEqual(prevProps: EnumFormProps, nextProps: EnumFormProps) {
80
+ return prevProps.enumValues.length === nextProps.enumValues.length &&
81
+ prevProps.onValuesChanged === nextProps.onValuesChanged &&
82
+ prevProps.getData === nextProps.getData
83
+ ;
84
+ }
85
+ );
86
+
87
+ type EnumFormFieldsProps = {
88
+ values: { enumValues: EnumValueConfig[] };
89
+ errors: any;
90
+ enumValuesPath: string;
91
+ shouldUpdateId: boolean;
92
+ disabled: boolean;
93
+ getData?: () => Promise<string[]>;
94
+ allowDataInference?: boolean;
95
+ };
96
+
97
+ // const EnumFormFields = React.memo(
98
+ function EnumFormFields({
99
+ values,
100
+ errors,
101
+ disabled,
102
+ enumValuesPath,
103
+ shouldUpdateId,
104
+ allowDataInference,
105
+ getData,
106
+ }: EnumFormFieldsProps) {
107
+
108
+ const {
109
+ setFieldValue
110
+ } = useFormikContext();
111
+
112
+ const [lastInternalIdAdded, setLastInternalIdAdded] = React.useState<number | undefined>();
113
+ const [editDialogIndex, setEditDialogIndex] = React.useState<number | undefined>();
114
+ const [inferring, setInferring] = React.useState(false);
115
+
116
+ const inferredValuesRef = React.useRef(new Set());
117
+ const inferredValues = inferredValuesRef.current;
118
+
119
+ const buildEntry = (index: number, internalId: number) => {
120
+ const justAdded = lastInternalIdAdded === internalId;
121
+ return <EnumEntry index={index}
122
+ disabled={disabled}
123
+ enumValuesPath={enumValuesPath}
124
+ autoFocus={justAdded}
125
+ shouldUpdateId={shouldUpdateId || justAdded}
126
+ onDialogOpen={() => setEditDialogIndex(index)}
127
+ inferredEntry={inferredValues.has(values.enumValues[index]?.id as string)}
128
+ key={`${internalId}`}/>;
129
+ };
130
+
131
+ const inferValues = async () => {
132
+ if (!getData)
133
+ return;
134
+ setInferring(true);
135
+ getData?.().then((data) => {
136
+ if (!data)
137
+ return;
138
+
139
+ const flatData = data.flat();
140
+
141
+ const fieldData = Array.from(new Set(flatData));
142
+
143
+ const currentEnumValues = values.enumValues;
144
+ const foundEnumValues = extractEnumFromValues(fieldData);
145
+
146
+ // add only new enum values
147
+ const newEnumValues = foundEnumValues.filter((enumValue) => {
148
+ return !currentEnumValues?.some((v: any) => v.id === enumValue.id);
149
+ });
150
+
151
+ newEnumValues.forEach((enumValue) => {
152
+ inferredValues.add(enumValue.id);
153
+ });
154
+ setFieldValue(enumValuesPath, [...newEnumValues, ...currentEnumValues]);
155
+ }).catch(e => {
156
+ console.error(e);
157
+ })
158
+ .finally(() => setInferring(false));
159
+ }
160
+
161
+ return (
162
+ <div className={"col-span-12"}>
163
+ <div className="ml-3.5 flex flex-row items-center">
164
+ <ListIcon/>
165
+ <Typography variant={"subtitle2"}
166
+ className="ml-2 grow">
167
+ Values
168
+ </Typography>
169
+ {allowDataInference &&
170
+ <Button loading={inferring}
171
+ disabled={disabled || inferring}
172
+ variant={"text"}
173
+ size={"small"}
174
+ onClick={inferValues}>
175
+ {inferring ? <CircularProgress size={"small"}/> : <AutoAwesomeIcon/>}
176
+ Infer values from data
177
+ </Button>}
178
+ </div>
179
+
180
+ <Paper className="p-4 m-1">
181
+
182
+ <FormikArrayContainer
183
+ value={values.enumValues}
184
+ addLabel={"Add enum value"}
185
+ name={enumValuesPath}
186
+ buildEntry={buildEntry}
187
+ disabled={disabled}
188
+ onInternalIdAdded={setLastInternalIdAdded}
189
+ small={true}
190
+ setFieldValue={setFieldValue}
191
+ includeAddButton={true}/>
192
+
193
+ <EnumEntryDialog index={editDialogIndex}
194
+ open={editDialogIndex !== undefined}
195
+ enumValuesPath={enumValuesPath}
196
+ onClose={() => setEditDialogIndex(undefined)}/>
197
+ </Paper>
198
+ </div>
199
+ );
200
+ }
201
+
202
+ type EnumEntryProps = {
203
+ index: number,
204
+ enumValuesPath: string,
205
+ shouldUpdateId: boolean,
206
+ autoFocus: boolean,
207
+ onDialogOpen: () => void;
208
+ disabled: boolean;
209
+ inferredEntry?: boolean;
210
+ };
211
+
212
+ const EnumEntry = React.memo(
213
+ function EnumEntryInternal({
214
+ index,
215
+ shouldUpdateId: updateId,
216
+ enumValuesPath,
217
+ autoFocus,
218
+ onDialogOpen,
219
+ disabled,
220
+ inferredEntry
221
+ }: EnumEntryProps) {
222
+
223
+ const {
224
+ values,
225
+ handleChange,
226
+ errors,
227
+ setFieldValue,
228
+ touched
229
+ } = useFormikContext<EnumValues>();
230
+
231
+ const shouldUpdateIdRef = React.useRef(!getIn(values, `${enumValuesPath}[${index}].id`));
232
+ const shouldUpdateId = updateId || shouldUpdateIdRef.current;
233
+
234
+ const idValue = getIn(values, `${enumValuesPath}[${index}].id`);
235
+ const labelValue = getIn(values, `${enumValuesPath}[${index}].label`);
236
+
237
+ const labelError = getIn(errors, `${enumValuesPath}[${index}].label`);
238
+
239
+ const currentLabelRef = React.useRef(labelValue);
240
+
241
+ React.useEffect(() => {
242
+ if ((currentLabelRef.current === idValue || !idValue) && shouldUpdateId) {
243
+ setFieldValue(`${enumValuesPath}[${index}].id`, labelValue);
244
+ }
245
+ currentLabelRef.current = labelValue;
246
+ }, [labelValue]);
247
+
248
+ return (
249
+ <div className={"flex w-full align-center justify-center"}>
250
+ <FastField name={`${enumValuesPath}[${index}].label`}
251
+ as={DebouncedTextField}
252
+ className={"flex-grow"}
253
+ required
254
+ disabled={disabled}
255
+ size="small"
256
+ validate={validateLabel}
257
+ autoFocus={autoFocus}
258
+ autoComplete="off"
259
+ endAdornment={inferredEntry && <AutoAwesomeIcon size={"small"}/>}
260
+ error={Boolean(labelError)}/>
261
+
262
+ {!disabled &&
263
+ <IconButton
264
+ size="small"
265
+ aria-label="edit"
266
+ className={"m-1"}
267
+ onClick={() => onDialogOpen()}>
268
+ <SettingsIcon size={"small"}/>
269
+ </IconButton>}
270
+
271
+ </div>);
272
+ },
273
+ function areEqual(prevProps: EnumEntryProps, nextProps: EnumEntryProps) {
274
+ return prevProps.index === nextProps.index &&
275
+ prevProps.enumValuesPath === nextProps.enumValuesPath &&
276
+ prevProps.shouldUpdateId === nextProps.shouldUpdateId &&
277
+ prevProps.inferredEntry === nextProps.inferredEntry &&
278
+ prevProps.autoFocus === nextProps.autoFocus;
279
+ }
280
+ );
281
+
282
+ function EnumEntryDialog({
283
+ index,
284
+ open,
285
+ onClose,
286
+ enumValuesPath
287
+ }: {
288
+ index?: number;
289
+ open: boolean;
290
+ enumValuesPath: string;
291
+ onClose: () => void;
292
+ }) {
293
+
294
+ const {
295
+ values,
296
+ handleChange,
297
+ errors,
298
+ setFieldValue,
299
+ touched
300
+ } = useFormikContext<EnumValues>();
301
+
302
+ const idError = index !== undefined ? getIn(errors, `${enumValuesPath}[${index}].id`) : undefined;
303
+ return <Dialog
304
+ maxWidth="md"
305
+ aria-labelledby="enum-edit-dialog"
306
+ open={open}
307
+ onOpenChange={(open) => !open ? onClose() : undefined}
308
+ >
309
+
310
+ <DialogContent>
311
+ {index !== undefined &&
312
+ <div><FastField name={`${enumValuesPath}[${index}]id`}
313
+ as={DebouncedTextField}
314
+ required
315
+ validate={validateId}
316
+ label={"ID"}
317
+ size="small"
318
+ autoComplete="off"
319
+ error={Boolean(idError)}/>
320
+
321
+ <FieldHelperView error={Boolean(idError)}>
322
+ {idError ?? "Value saved in the data source"}
323
+ </FieldHelperView>
324
+ </div>}
325
+ </DialogContent>
326
+
327
+ <DialogActions>
328
+ <Button
329
+ autoFocus
330
+ variant="outlined"
331
+ onClick={onClose}
332
+ color="primary">
333
+ Ok
334
+ </Button>
335
+ </DialogActions>
336
+
337
+ </Dialog>
338
+ }
339
+
340
+ function validateLabel(value: string) {
341
+ let error;
342
+ if (!value) {
343
+ error = "You must specify a label";
344
+ }
345
+ return error;
346
+ }
347
+
348
+ function validateId(value: string) {
349
+ let error;
350
+ if (!value) {
351
+ error = "You must specify an ID";
352
+ }
353
+ return error;
354
+ }
@@ -0,0 +1,110 @@
1
+ import { EntityCollection, useSnackbarController } from "@firecms/core";
2
+ import { Button, ContentCopyIcon, Dialog, DialogActions, DialogContent, Typography, } from "@firecms/ui";
3
+ import React from "react";
4
+ import JSON5 from "json5";
5
+ import { Highlight, themes } from "prism-react-renderer"
6
+ import { camelCase } from "./utils/strings";
7
+
8
+ export function GetCodeDialog({ collection, onOpenChange, open }: { onOpenChange: (open: boolean) => void, collection: any, open: any }) {
9
+
10
+ const snackbarController = useSnackbarController();
11
+
12
+ const code = "import { EntityCollection } from \"firecms\";\n\nconst " + camelCase(collection.name) + "Collection:EntityCollection = " + JSON5.stringify(collectionToCode(collection), null, "\t");
13
+ return <Dialog open={open}
14
+ onOpenChange={onOpenChange}
15
+ maxWidth={"4xl"}>
16
+ <DialogContent>
17
+ <Typography variant={"h6"} className={"my-4"}>
18
+ Code for {collection.name}
19
+ </Typography>
20
+ <Typography variant={"body2"} className={"my-4 mb-8"}>
21
+ If you want to customise the collection in code, you can add this collection code to your CMS
22
+ app configuration.
23
+ More info in the <a
24
+ rel="noopener noreferrer"
25
+ href={"https://firecms.co/docs/customization_quickstart"}>docs</a>.
26
+ </Typography>
27
+ <Highlight
28
+ theme={themes.vsDark}
29
+ code={code}
30
+ language="typescript"
31
+ >
32
+ {({ className, style, tokens, getLineProps, getTokenProps }) => (
33
+ <pre style={style} className={"p-4 rounded text-sm"}>
34
+ {tokens.map((line, i) => (
35
+ <div key={i} {...getLineProps({ line })}>
36
+ {line.map((token, key) => (
37
+ <span key={key} {...getTokenProps({ token })} />
38
+ ))}
39
+ </div>
40
+ ))}
41
+ </pre>
42
+ )}
43
+ </Highlight>
44
+
45
+ </DialogContent>
46
+ <DialogActions>
47
+ <Button
48
+ variant={"text"}
49
+ size={"small"}
50
+ onClick={(e) => {
51
+ e.stopPropagation();
52
+ e.preventDefault();
53
+ snackbarController.open({
54
+ type: "success",
55
+ message: `Copied`
56
+ })
57
+ return navigator.clipboard.writeText(code);
58
+ }}>
59
+ <ContentCopyIcon size={"small"}/>
60
+ Copy to clipboard
61
+ </Button>
62
+ <Button onClick={() => onOpenChange(false)}>Close</Button>
63
+ </DialogActions>
64
+ </Dialog>;
65
+ }
66
+
67
+ function collectionToCode(collection: EntityCollection): object {
68
+
69
+ const propertyCleanup = (property: any) => {
70
+
71
+ const updatedProperty = {
72
+ ...property
73
+ };
74
+
75
+ delete updatedProperty.fromBuilder;
76
+ delete updatedProperty.resolved;
77
+ delete updatedProperty.propertiesOrder;
78
+ delete updatedProperty.editable;
79
+
80
+ if (updatedProperty.type === "map") {
81
+ return {
82
+ ...updatedProperty,
83
+ properties: updatedProperty.properties.map(propertyCleanup)
84
+ }
85
+ }
86
+ return updatedProperty;
87
+ }
88
+
89
+ return {
90
+ id: collection.id,
91
+ name: collection.name,
92
+ singularName: collection.singularName,
93
+ path: collection.path,
94
+ description: collection.description,
95
+ editable: true,
96
+ collectionGroup: collection.collectionGroup,
97
+ icon: collection.icon,
98
+ group: collection.group,
99
+ customId: collection.customId,
100
+ initialFilter: collection.initialFilter,
101
+ initialSort: collection.initialSort,
102
+ properties: Object.entries(collection.properties)
103
+ .map(([key, value]) => ({
104
+ [key]: propertyCleanup(value)
105
+ }))
106
+ .reduce((a, b) => ({ ...a, ...b }), {}),
107
+ subcollections: (collection.subcollections ?? []).map(collectionToCode)
108
+ }
109
+
110
+ }