@firecms/collection_editor 3.0.0-alpha.8 → 3.0.0-alpha.81

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