@firecms/collection_editor 3.0.0-alpha.9 → 3.0.0-beta.10

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 (149) hide show
  1. package/LICENSE +114 -21
  2. package/dist/ConfigControllerProvider.d.ts +13 -3
  3. package/dist/index.d.ts +4 -2
  4. package/dist/index.es.js +5587 -4976
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/index.umd.js +6829 -1
  7. package/dist/index.umd.js.map +1 -1
  8. package/dist/types/collection_editor_controller.d.ts +36 -8
  9. package/dist/types/collection_inference.d.ts +1 -1
  10. package/dist/types/config_controller.d.ts +33 -6
  11. package/dist/types/persisted_collection.d.ts +4 -2
  12. package/dist/ui/CollectionViewHeaderAction.d.ts +11 -0
  13. package/dist/{components → ui}/EditorCollectionAction.d.ts +1 -1
  14. package/dist/ui/EditorCollectionActionStart.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/PropertyAddColumnComponent.d.ts +8 -0
  18. package/dist/{components → ui}/collection_editor/CollectionDetailsForm.d.ts +3 -2
  19. package/dist/{components → ui}/collection_editor/CollectionEditorDialog.d.ts +15 -11
  20. package/dist/{components → ui}/collection_editor/CollectionEditorWelcomeView.d.ts +3 -3
  21. package/dist/{components → ui}/collection_editor/CollectionPropertiesEditorForm.d.ts +8 -6
  22. package/dist/{components → ui}/collection_editor/CollectionYupValidation.d.ts +3 -0
  23. package/dist/ui/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
  24. package/dist/{components → ui}/collection_editor/EnumForm.d.ts +1 -2
  25. package/dist/ui/collection_editor/GetCodeDialog.d.ts +5 -0
  26. package/dist/{components → ui}/collection_editor/PropertyEditView.d.ts +21 -11
  27. package/dist/{components → ui}/collection_editor/PropertyFieldPreview.d.ts +4 -3
  28. package/dist/{components → ui}/collection_editor/PropertyTree.d.ts +11 -7
  29. package/dist/{components → ui}/collection_editor/SubcollectionsEditTab.d.ts +3 -3
  30. package/dist/ui/collection_editor/SwitchControl.d.ts +8 -0
  31. package/dist/{components → ui}/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
  32. package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +14 -0
  33. package/dist/{components → ui}/collection_editor/import/clean_import_data.d.ts +1 -1
  34. package/dist/{components → ui}/collection_editor/properties/BlockPropertyField.d.ts +4 -1
  35. package/dist/{components → ui}/collection_editor/properties/CommonPropertyFields.d.ts +1 -1
  36. package/dist/{components → ui}/collection_editor/properties/MapPropertyField.d.ts +4 -1
  37. package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
  38. package/dist/{components → ui}/collection_editor/properties/RepeatPropertyField.d.ts +4 -1
  39. package/dist/{components → ui}/collection_editor/properties/StringPropertyField.d.ts +1 -1
  40. package/dist/ui/collection_editor/properties/UrlPropertyField.d.ts +4 -0
  41. package/dist/ui/collection_editor/templates/blog_template.d.ts +2 -0
  42. package/dist/ui/collection_editor/templates/pages_template.d.ts +2 -0
  43. package/dist/ui/collection_editor/templates/products_template.d.ts +2 -0
  44. package/dist/ui/collection_editor/templates/users_template.d.ts +2 -0
  45. package/dist/{components → ui}/collection_editor/util.d.ts +1 -0
  46. package/dist/ui/collection_editor/utils/strings.d.ts +1 -0
  47. package/dist/ui/collection_editor/utils/supported_fields.d.ts +3 -0
  48. package/dist/ui/collection_editor/utils/update_property_for_widget.d.ts +2 -0
  49. package/dist/useCollectionEditorPlugin.d.ts +17 -6
  50. package/dist/utils/collections.d.ts +6 -0
  51. package/dist/utils/entities.d.ts +3 -4
  52. package/package.json +35 -37
  53. package/src/ConfigControllerProvider.tsx +350 -0
  54. package/src/index.ts +36 -0
  55. package/src/types/collection_editor_controller.tsx +53 -0
  56. package/src/types/collection_inference.ts +3 -0
  57. package/src/types/config_controller.tsx +60 -0
  58. package/src/types/config_permissions.ts +20 -0
  59. package/src/types/persisted_collection.ts +9 -0
  60. package/src/ui/CollectionViewHeaderAction.tsx +48 -0
  61. package/src/ui/EditorCollectionAction.tsx +56 -0
  62. package/src/ui/EditorCollectionActionStart.tsx +88 -0
  63. package/src/ui/HomePageEditorCollectionAction.tsx +89 -0
  64. package/src/ui/MissingReferenceWidget.tsx +37 -0
  65. package/src/ui/NewCollectionButton.tsx +18 -0
  66. package/src/ui/NewCollectionCard.tsx +48 -0
  67. package/src/ui/PropertyAddColumnComponent.tsx +47 -0
  68. package/src/ui/collection_editor/CollectionDetailsForm.tsx +426 -0
  69. package/src/ui/collection_editor/CollectionEditorDialog.tsx +826 -0
  70. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +214 -0
  71. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +513 -0
  72. package/src/ui/collection_editor/CollectionYupValidation.tsx +7 -0
  73. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +37 -0
  74. package/src/ui/collection_editor/EnumForm.tsx +357 -0
  75. package/src/ui/collection_editor/GetCodeDialog.tsx +122 -0
  76. package/src/ui/collection_editor/PropertyEditView.tsx +789 -0
  77. package/src/ui/collection_editor/PropertyFieldPreview.tsx +204 -0
  78. package/src/ui/collection_editor/PropertyTree.tsx +254 -0
  79. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +269 -0
  80. package/src/ui/collection_editor/SwitchControl.tsx +39 -0
  81. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +47 -0
  82. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +53 -0
  83. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +299 -0
  84. package/src/ui/collection_editor/import/clean_import_data.ts +53 -0
  85. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +144 -0
  86. package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +40 -0
  87. package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +110 -0
  88. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +89 -0
  89. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +114 -0
  90. package/src/ui/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
  91. package/src/ui/collection_editor/properties/MapPropertyField.tsx +150 -0
  92. package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
  93. package/src/ui/collection_editor/properties/NumberPropertyField.tsx +38 -0
  94. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +160 -0
  95. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +108 -0
  96. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +215 -0
  97. package/src/ui/collection_editor/properties/StringPropertyField.tsx +70 -0
  98. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +89 -0
  99. package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +45 -0
  100. package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
  101. package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +61 -0
  102. package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +115 -0
  103. package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +150 -0
  104. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
  105. package/src/ui/collection_editor/templates/blog_template.ts +115 -0
  106. package/src/ui/collection_editor/templates/pages_template.ts +183 -0
  107. package/src/ui/collection_editor/templates/products_template.ts +88 -0
  108. package/src/ui/collection_editor/templates/users_template.ts +42 -0
  109. package/src/ui/collection_editor/util.ts +28 -0
  110. package/src/ui/collection_editor/utils/strings.ts +9 -0
  111. package/src/ui/collection_editor/utils/supported_fields.tsx +29 -0
  112. package/src/ui/collection_editor/utils/update_property_for_widget.ts +271 -0
  113. package/src/ui/collection_editor/utils/useTraceUpdate.tsx +23 -0
  114. package/src/useCollectionEditorController.tsx +9 -0
  115. package/src/useCollectionEditorPlugin.tsx +164 -0
  116. package/src/useCollectionsConfigController.tsx +9 -0
  117. package/src/utils/arrays.ts +3 -0
  118. package/src/utils/collections.ts +30 -0
  119. package/src/utils/entities.ts +38 -0
  120. package/src/vite-env.d.ts +1 -0
  121. package/dist/components/collection_editor/PropertySelectItem.d.ts +0 -8
  122. package/dist/components/collection_editor/SelectIcons.d.ts +0 -6
  123. package/dist/components/collection_editor/import/CollectionEditorImportMapping.d.ts +0 -4
  124. package/dist/components/collection_editor/properties/FieldHelperView.d.ts +0 -4
  125. package/dist/components/collection_editor/templates/blog_template.d.ts +0 -10
  126. package/dist/components/collection_editor/templates/products_template.d.ts +0 -12
  127. package/dist/components/collection_editor/templates/users_template.d.ts +0 -7
  128. package/dist/components/collection_editor/utils/supported_fields.d.ts +0 -3
  129. package/dist/components/collection_editor/utils/update_property_for_widget.d.ts +0 -3
  130. package/dist/types/editable_properties.d.ts +0 -10
  131. package/dist/utils/icons.d.ts +0 -2
  132. package/dist/utils/synonyms.d.ts +0 -1951
  133. /package/dist/{components → ui}/HomePageEditorCollectionAction.d.ts +0 -0
  134. /package/dist/{components → ui}/NewCollectionCard.d.ts +0 -0
  135. /package/dist/{components → ui}/collection_editor/UnsavedChangesDialog.d.ts +0 -0
  136. /package/dist/{components → ui}/collection_editor/properties/BooleanPropertyField.d.ts +0 -0
  137. /package/dist/{components → ui}/collection_editor/properties/DateTimePropertyField.d.ts +0 -0
  138. /package/dist/{components → ui}/collection_editor/properties/EnumPropertyField.d.ts +0 -0
  139. /package/dist/{components → ui}/collection_editor/properties/KeyValuePropertyField.d.ts +0 -0
  140. /package/dist/{components → ui}/collection_editor/properties/NumberPropertyField.d.ts +0 -0
  141. /package/dist/{components → ui}/collection_editor/properties/ReferencePropertyField.d.ts +0 -0
  142. /package/dist/{components → ui}/collection_editor/properties/StoragePropertyField.d.ts +0 -0
  143. /package/dist/{components → ui}/collection_editor/properties/advanced/AdvancedPropertyValidation.d.ts +0 -0
  144. /package/dist/{components → ui}/collection_editor/properties/validation/ArrayPropertyValidation.d.ts +0 -0
  145. /package/dist/{components → ui}/collection_editor/properties/validation/GeneralPropertyValidation.d.ts +0 -0
  146. /package/dist/{components → ui}/collection_editor/properties/validation/NumberPropertyValidation.d.ts +0 -0
  147. /package/dist/{components → ui}/collection_editor/properties/validation/StringPropertyValidation.d.ts +0 -0
  148. /package/dist/{components → ui}/collection_editor/properties/validation/ValidationPanel.d.ts +0 -0
  149. /package/dist/{components → ui}/collection_editor/utils/useTraceUpdate.d.ts +0 -0
@@ -0,0 +1,299 @@
1
+ import {
2
+ DataNewPropertiesMapping,
3
+ getInferenceType,
4
+ ImportConfig,
5
+ ImportNewPropertyFieldPreview
6
+ } from "@firecms/data_import_export";
7
+ import { getIn, useFormex } from "@firecms/formex";
8
+
9
+ import { getFieldConfig, getFieldId, Properties, Property, PropertyConfig, PropertyConfigBadge, } from "@firecms/core";
10
+ import { cls, Container, Select, SelectItem, Tooltip, Typography } from "@firecms/ui";
11
+ import React, { useState } from "react";
12
+ import { OnPropertyChangedParams, PropertyFormDialog, PropertyWithId } from "../PropertyEditView";
13
+ import { getFullId, idToPropertiesPath, namespaceToPropertiesOrderPath } from "../util";
14
+ import { PersistedCollection } from "../../../types/persisted_collection";
15
+ import { updatePropertyFromWidget } from "../utils/update_property_for_widget";
16
+ import { supportedFields } from "../utils/supported_fields";
17
+ import { buildPropertyFromData } from "@firecms/schema_inference";
18
+
19
+ export function CollectionEditorImportMapping({
20
+ importConfig,
21
+ propertyConfigs,
22
+ collectionEditable
23
+ }:
24
+ {
25
+ importConfig: ImportConfig,
26
+ propertyConfigs: Record<string, PropertyConfig>,
27
+ collectionEditable: boolean
28
+ }) {
29
+
30
+ // const {
31
+ // values,
32
+ // setFieldValue,
33
+ // setFieldTouched,
34
+ // setValues,
35
+ // submitCount
36
+ // } = useFormex();
37
+
38
+ const {
39
+ setFieldValue,
40
+ setFieldTouched,
41
+ values
42
+ } = useFormex<PersistedCollection>();
43
+ const [selectedProperty, setSelectedProperty] = useState<PropertyWithId | undefined>(undefined);
44
+
45
+ const currentPropertiesOrderRef = React.useRef<{
46
+ [key: string]: string[]
47
+ }>(values.propertiesOrder ? { "": values.propertiesOrder } : {});
48
+
49
+ const propertyKey = selectedProperty ? selectedProperty.id : undefined;
50
+ const property = selectedProperty || undefined;
51
+
52
+ const onPropertyChanged = ({
53
+ id,
54
+ property,
55
+ previousId,
56
+ namespace
57
+ }: OnPropertyChangedParams) => {
58
+
59
+ const fullId = id ? getFullId(id, namespace) : undefined;
60
+ const propertyPath = fullId ? idToPropertiesPath(fullId) : undefined;
61
+
62
+ // setSelectedProperty(property);
63
+ const getCurrentPropertiesOrder = (namespace?: string) => {
64
+ if (!namespace) return currentPropertiesOrderRef.current[""];
65
+ return currentPropertiesOrderRef.current[namespace] ?? getIn(values, namespaceToPropertiesOrderPath(namespace));
66
+ }
67
+
68
+ const updatePropertiesOrder = (newPropertiesOrder: string[], namespace?: string) => {
69
+ const propertiesOrderPath = namespaceToPropertiesOrderPath(namespace);
70
+
71
+ setFieldValue(propertiesOrderPath, newPropertiesOrder, false);
72
+ currentPropertiesOrderRef.current[namespace ?? ""] = newPropertiesOrder;
73
+
74
+ };
75
+
76
+ // If the id has changed we need to a little cleanup
77
+ if (previousId && previousId !== id) {
78
+ const previousFullId = getFullId(previousId, namespace);
79
+ const previousPropertyPath = idToPropertiesPath(previousFullId);
80
+
81
+ const currentPropertiesOrder = getCurrentPropertiesOrder(namespace);
82
+
83
+ // replace previousId with id in propertiesOrder
84
+ const newPropertiesOrder = currentPropertiesOrder
85
+ .map((p) => p === previousId ? id : p)
86
+ .filter((p) => p !== undefined) as string[];
87
+ updatePropertiesOrder(newPropertiesOrder, namespace);
88
+
89
+ // replace previousId with id in headersMapping
90
+ const newHeadersMapping = { ...importConfig.headersMapping };
91
+ Object.keys(newHeadersMapping).forEach((key) => {
92
+ if (newHeadersMapping[key] === previousId) {
93
+ newHeadersMapping[key] = id ?? "";
94
+ }
95
+ });
96
+ importConfig.setHeadersMapping(newHeadersMapping);
97
+
98
+ // if (id) {
99
+ // setSelectedPropertyIndex(newPropertiesOrder.indexOf(id));
100
+ // setSelectedPropertyKey(id);
101
+ // }
102
+ setFieldValue(previousPropertyPath, undefined, false);
103
+ setFieldTouched(previousPropertyPath, false, false);
104
+ }
105
+
106
+ if (propertyPath) {
107
+ setFieldValue(propertyPath, property, false);
108
+ setFieldTouched(propertyPath, true, false);
109
+ }
110
+ };
111
+ const onPropertyTypeChanged = async ({
112
+ id,
113
+ importKey,
114
+ property,
115
+ namespace
116
+ }: OnPropertyChangedParams & {
117
+ importKey: string
118
+ }) => {
119
+
120
+ const fullId = id ? getFullId(id, namespace) : undefined;
121
+ const propertyPath = fullId ? idToPropertiesPath(fullId) : undefined;
122
+
123
+ // we try to infer the rest of the properties of a property, from the type and the data
124
+ const propertyData = importConfig.importData.map((d) => getIn(d, importKey));
125
+ const inferredNewProperty = {
126
+ ...buildPropertyFromData(propertyData, property, getInferenceType),
127
+ editable: true
128
+ };
129
+
130
+ if (propertyPath) {
131
+ if (inferredNewProperty) {
132
+ setFieldValue(propertyPath, inferredNewProperty, false);
133
+ } else {
134
+ setFieldValue(propertyPath, property, false);
135
+ }
136
+ setFieldTouched(propertyPath, true, false);
137
+ }
138
+ };
139
+
140
+ return (
141
+
142
+ <div className={"overflow-auto my-auto"}>
143
+ <Container maxWidth={"6xl"} className={"flex flex-col gap-4 p-8 m-auto"}>
144
+
145
+ <Typography variant="h6" className={"my-4 ml-3.5"}>Data property mapping</Typography>
146
+
147
+ <DataNewPropertiesMapping importConfig={importConfig}
148
+ destinationProperties={values.properties as Properties}
149
+ buildPropertyView={({
150
+ property,
151
+ propertyKey,
152
+ importKey,
153
+ isIdColumn
154
+ }) => {
155
+ if (isIdColumn) {
156
+ return <Typography> This column will be used as ID</Typography>
157
+ }
158
+
159
+ return <ImportNewPropertyFieldPreview
160
+ property={property}
161
+ propertyKey={propertyKey}
162
+ onPropertyNameChanged={(propertyKey: string, value: string) => setFieldValue(`properties.${propertyKey}.name`, value, false)}
163
+ onEditClick={() => {
164
+ if (!propertyKey || !property) return;
165
+ setSelectedProperty({
166
+ ...property,
167
+ id: propertyKey,
168
+ editable: true
169
+ });
170
+ }}
171
+ propertyTypeView={<PropertySelect property={property}
172
+ disabled={false}
173
+ onPropertyChanged={(props) => onPropertyTypeChanged({
174
+ ...props,
175
+ importKey
176
+ })}
177
+ propertyKey={propertyKey}
178
+ propertyConfigs={propertyConfigs}/>}
179
+ />;
180
+ }}/>
181
+ </Container>
182
+
183
+ <PropertyFormDialog
184
+ open={selectedProperty !== undefined}
185
+ propertyKey={propertyKey}
186
+ property={property}
187
+ inArray={false}
188
+ autoUpdateId={false}
189
+ onPropertyChanged={onPropertyChanged}
190
+ allowDataInference={false}
191
+ collectionEditable={collectionEditable}
192
+ onOkClicked={() => {
193
+ setSelectedProperty(undefined);
194
+ }}
195
+ onCancel={() => {
196
+ setSelectedProperty(undefined);
197
+ }}
198
+ autoOpenTypeSelect={false}
199
+ existingProperty={false}
200
+ propertyConfigs={propertyConfigs}/>
201
+
202
+ <div style={{ height: "52px" }}/>
203
+ </div>
204
+ );
205
+
206
+ }
207
+
208
+ function PropertySelect({
209
+ property,
210
+ onPropertyChanged,
211
+ propertyKey,
212
+ propertyConfigs,
213
+ disabled
214
+ }: {
215
+ property: Property | null,
216
+ propertyKey: string | null,
217
+ onPropertyChanged: ({
218
+ id,
219
+ property,
220
+ previousId,
221
+ namespace
222
+ }: OnPropertyChangedParams) => void,
223
+ propertyConfigs: Record<string, PropertyConfig>,
224
+ disabled?: boolean
225
+ }) {
226
+
227
+ const fieldId = property ? getFieldId(property) : null;
228
+ const widget = property ? getFieldConfig(property, propertyConfigs) : null;
229
+
230
+ const [selectOpen, setSelectOpen] = useState(false);
231
+
232
+ return <Tooltip title={property && widget ? `${widget?.name} - ${property.dataType}` : undefined}
233
+ open={selectOpen ? false : undefined}>
234
+ <Select
235
+ open={selectOpen}
236
+ onOpenChange={setSelectOpen}
237
+ invisible={true}
238
+ className={"w-full"}
239
+ disabled={disabled}
240
+ error={!widget}
241
+ value={fieldId ?? ""}
242
+ placeholder={"Select a property widget"}
243
+ position={"item-aligned"}
244
+ renderValue={(value) => {
245
+ if (!widget) return null;
246
+ return <PropertyConfigBadge propertyConfig={widget}/>
247
+ }}
248
+ onValueChange={(newSelectedWidgetId) => {
249
+ const newProperty = updatePropertyFromWidget(property, newSelectedWidgetId, propertyConfigs)
250
+ if (!propertyKey) return;
251
+ onPropertyChanged({
252
+ id: propertyKey,
253
+ property: newProperty,
254
+ previousId: propertyKey,
255
+ namespace: undefined
256
+ });
257
+ }}>
258
+ {Object.entries(supportedFields).map(([key, widget]) => {
259
+ return <PropertySelectItem
260
+ key={key}
261
+ value={key}
262
+ optionDisabled={false}
263
+ propertyConfig={widget}
264
+ existing={false}/>;
265
+ })
266
+ }
267
+ </Select>
268
+ </Tooltip>;
269
+ }
270
+
271
+ export interface PropertySelectItemProps {
272
+ value: string;
273
+ optionDisabled: boolean;
274
+ propertyConfig: PropertyConfig;
275
+ existing: boolean;
276
+ }
277
+
278
+ export function PropertySelectItem({ value, optionDisabled, propertyConfig, existing }: PropertySelectItemProps) {
279
+ return <SelectItem value={value}
280
+ disabled={optionDisabled}
281
+ className={"flex flex-row items-center"}>
282
+ <div
283
+ className={cls(
284
+ "flex flex-row items-center text-base min-h-[48px]",
285
+ optionDisabled ? "w-full" : "")}>
286
+ <div className={"mr-8"}>
287
+ <PropertyConfigBadge propertyConfig={propertyConfig}/>
288
+ </div>
289
+ <div>
290
+ <div>{propertyConfig.name}</div>
291
+ <Typography variant={"caption"}
292
+ color={"disabled"}
293
+ className={"max-w-sm"}>
294
+ {existing && optionDisabled ? "You can only switch to widgets that use the same data type" : propertyConfig.description}
295
+ </Typography>
296
+ </div>
297
+ </div>
298
+ </SelectItem>
299
+ }
@@ -0,0 +1,53 @@
1
+ import { Properties, slugify } from "@firecms/core";
2
+ import { ImportConfig } from "@firecms/data_import_export";
3
+
4
+ export function cleanPropertiesFromImport(properties: Properties, parentSlug = ""): {
5
+ headersMapping: ImportConfig["headersMapping"],
6
+ properties: Properties,
7
+ idColumn?: ImportConfig["idColumn"],
8
+ } {
9
+
10
+ const result = Object.keys(properties).reduce((acc, key) => {
11
+ const property = properties[key];
12
+ const slug = slugify(key);
13
+ const fullSlug = parentSlug ? `${parentSlug}.${slug}` : slug;
14
+
15
+ if (property.dataType === "map" && property.properties) {
16
+ const slugifiedResult = cleanPropertiesFromImport(property.properties as Properties, fullSlug);
17
+ return {
18
+ headersMapping: { ...acc.headersMapping, [key]: fullSlug },
19
+ properties: {
20
+ ...acc.properties,
21
+ [slug]: {
22
+ ...property,
23
+ properties: slugifiedResult.properties,
24
+ propertiesOrder: Object.keys(slugifiedResult.properties)
25
+ }
26
+ }
27
+ }
28
+ }
29
+
30
+ const updatedProperties = {
31
+ ...acc.properties,
32
+ [slug]: property
33
+ } as Properties;
34
+
35
+ const headersMapping = { ...acc.headersMapping, [key]: fullSlug } as Record<string, string>;
36
+
37
+ return {
38
+ headersMapping,
39
+ properties: updatedProperties,
40
+ }
41
+ }, { headersMapping: {}, properties: {} });
42
+
43
+ const firstKey = Object.keys(result.headersMapping)?.[0];
44
+ let idColumn: string | undefined;
45
+ if (firstKey?.includes("id") || firstKey?.includes("key")) {
46
+ idColumn = firstKey;
47
+ }
48
+
49
+ return {
50
+ ...result,
51
+ idColumn
52
+ };
53
+ }
@@ -0,0 +1,144 @@
1
+ import React, { useState } from "react";
2
+ import { AddIcon, Button, Paper, Typography } from "@firecms/ui";
3
+ import { getIn, useFormex } from "@firecms/formex";
4
+ import { PropertyFormDialog } from "../PropertyEditView";
5
+ import { getFullId, idToPropertiesPath, namespaceToPropertiesOrderPath, namespaceToPropertiesPath } from "../util";
6
+ import { PropertyTree } from "../PropertyTree";
7
+ import { ArrayProperty, Property, PropertyConfig } from "@firecms/core";
8
+
9
+ export function BlockPropertyField({
10
+ disabled,
11
+ getData,
12
+ allowDataInference,
13
+ propertyConfigs,
14
+ collectionEditable
15
+ }: {
16
+ disabled: boolean;
17
+ getData?: () => Promise<object[]>;
18
+ allowDataInference: boolean;
19
+ propertyConfigs: Record<string, PropertyConfig>,
20
+ collectionEditable: boolean;
21
+ }) {
22
+
23
+ const {
24
+ values,
25
+ setFieldValue
26
+ } = useFormex<ArrayProperty>();
27
+
28
+ const [propertyDialogOpen, setPropertyDialogOpen] = useState<boolean>(false);
29
+ const [selectedPropertyKey, setSelectedPropertyKey] = useState<string | undefined>();
30
+ const [selectedPropertyNamespace, setSelectedPropertyNamespace] = useState<string | undefined>();
31
+
32
+ const onPropertyChanged = ({
33
+ id,
34
+ property
35
+ }: { id?: string, property: Property }) => {
36
+ if (!id)
37
+ throw Error();
38
+
39
+ setFieldValue("oneOf.properties", {
40
+ ...(values.oneOf?.properties ?? {}),
41
+ [id]: property
42
+ }, false);
43
+ const currentPropertiesOrder = values.oneOf?.propertiesOrder ?? Object.keys(values.oneOf?.properties ?? {});
44
+ const newPropertiesOrder = currentPropertiesOrder.includes(id) ? currentPropertiesOrder : [...currentPropertiesOrder, id];
45
+ setFieldValue("oneOf.propertiesOrder", newPropertiesOrder, false);
46
+ setPropertyDialogOpen(false);
47
+ };
48
+
49
+ const selectedPropertyFullId = selectedPropertyKey ? getFullId(selectedPropertyKey, selectedPropertyNamespace) : undefined;
50
+ const selectedProperty = selectedPropertyFullId ? getIn(values.oneOf?.properties, selectedPropertyFullId.replaceAll(".", ".properties.")) : undefined;
51
+
52
+ const deleteProperty = (propertyKey?: string, namespace?: string) => {
53
+ const fullId = propertyKey ? getFullId(propertyKey, namespace) : undefined;
54
+ if (!fullId)
55
+ throw Error("collection editor miss config");
56
+
57
+ setFieldValue(`oneOf.${idToPropertiesPath(fullId)}`, undefined, false);
58
+ const propertiesOrderPath = `oneOf.${namespaceToPropertiesOrderPath(namespace)}`;
59
+ const currentPropertiesOrder: string[] = getIn(values, propertiesOrderPath) ?? Object.keys(getIn(values, namespaceToPropertiesPath(namespace)));
60
+ setFieldValue(propertiesOrderPath, currentPropertiesOrder.filter((p) => p !== propertyKey), false);
61
+
62
+ setPropertyDialogOpen(false);
63
+ setSelectedPropertyKey(undefined);
64
+ setSelectedPropertyNamespace(undefined);
65
+ };
66
+
67
+ const addChildButton = <Button
68
+ autoFocus
69
+ color="primary"
70
+
71
+ onClick={() => setPropertyDialogOpen(true)}
72
+ startIcon={<AddIcon/>}
73
+ >
74
+ Add property to {values.name ?? "this block"}
75
+ </Button>;
76
+
77
+ const onPropertyMove = (propertiesOrder: string[], namespace?: string) => {
78
+ setFieldValue(`oneOf.${namespaceToPropertiesOrderPath(namespace)}`, propertiesOrder, false);
79
+ };
80
+
81
+ return (
82
+ <>
83
+ <div className={"col-span-12"}>
84
+ <div className={"flex justify-between items-end mt-8 mb-4"}>
85
+ <Typography variant={"subtitle2"}>Properties in this
86
+ block</Typography>
87
+ {addChildButton}
88
+ </div>
89
+ <Paper className="p-2 pl-8">
90
+
91
+ <PropertyTree
92
+ properties={values.oneOf?.properties ?? {}}
93
+ propertiesOrder={values.oneOf?.propertiesOrder}
94
+ errors={{}}
95
+ collectionEditable={collectionEditable}
96
+ onPropertyClick={disabled
97
+ ? undefined
98
+ : (propertyKey, namespace) => {
99
+ setSelectedPropertyKey(propertyKey);
100
+ setSelectedPropertyNamespace(namespace);
101
+ setPropertyDialogOpen(true);
102
+ }}
103
+ onPropertyMove={disabled
104
+ ? undefined
105
+ : onPropertyMove}/>
106
+
107
+ {!disabled && !values.oneOf?.propertiesOrder?.length &&
108
+ <div className="h-full flex items-center justify-center p-4">
109
+ Add the first property to this block
110
+ </div>}
111
+
112
+ </Paper>
113
+ </div>
114
+
115
+ {!disabled && <PropertyFormDialog
116
+ inArray={false}
117
+ forceShowErrors={false}
118
+ open={propertyDialogOpen}
119
+ getData={getData}
120
+ allowDataInference={allowDataInference}
121
+ onCancel={() => {
122
+ setPropertyDialogOpen(false);
123
+ setSelectedPropertyKey(undefined);
124
+ setSelectedPropertyNamespace(undefined);
125
+ }}
126
+ onOkClicked={() => {
127
+ setPropertyDialogOpen(false);
128
+ setSelectedPropertyKey(undefined);
129
+ setSelectedPropertyNamespace(undefined);
130
+ }}
131
+ collectionEditable={collectionEditable}
132
+ onDelete={deleteProperty}
133
+ propertyKey={selectedPropertyKey}
134
+ propertyNamespace={selectedPropertyNamespace}
135
+ property={selectedProperty}
136
+ existingProperty={Boolean(selectedPropertyKey)}
137
+ autoUpdateId={!selectedPropertyKey}
138
+ autoOpenTypeSelect={!selectedPropertyKey}
139
+ onPropertyChanged={onPropertyChanged}
140
+ existingPropertyKeys={selectedPropertyKey ? undefined : values.oneOf?.propertiesOrder}
141
+ propertyConfigs={propertyConfigs}/>}
142
+
143
+ </>);
144
+ }
@@ -0,0 +1,40 @@
1
+ import React from "react";
2
+ import { Field, FormexFieldProps, getIn, useFormex } from "@firecms/formex";
3
+
4
+ import { GeneralPropertyValidation } from "./validation/GeneralPropertyValidation";
5
+ import { ValidationPanel } from "./validation/ValidationPanel";
6
+ import { SwitchControl } from "../SwitchControl";
7
+
8
+ export function BooleanPropertyField({ disabled }: {
9
+ disabled: boolean;
10
+ }) {
11
+ const { values } = useFormex();
12
+ const defaultValue = getIn(values, "defaultValue");
13
+
14
+ return (
15
+ <>
16
+ <div className={"col-span-12"}>
17
+
18
+ <ValidationPanel>
19
+ <GeneralPropertyValidation disabled={disabled}/>
20
+ </ValidationPanel>
21
+
22
+ </div>
23
+
24
+ <div className={"col-span-12"}>
25
+
26
+ <Field
27
+ name={"defaultValue"}>
28
+ {({ field, form }: FormexFieldProps) => {
29
+ return <SwitchControl
30
+ label={defaultValue === null || defaultValue === undefined ? "Default value not set" : ("Default value is " + defaultValue.toString())}
31
+ disabled={disabled}
32
+ allowIndeterminate={true} field={field}
33
+ form={form}/>
34
+ }}
35
+ </Field>
36
+
37
+ </div>
38
+ </>
39
+ );
40
+ }
@@ -0,0 +1,110 @@
1
+ import { Field, getIn, useFormex } from "@firecms/formex";
2
+ import { DebouncedTextField } from "@firecms/ui";
3
+ import { PropertyWithId } from "../PropertyEditView";
4
+ import React from "react";
5
+ import { FieldCaption, toSnakeCase, unslugify } from "@firecms/core";
6
+
7
+ type CommonPropertyFieldsProps = {
8
+ showErrors: boolean,
9
+ disabledId: boolean,
10
+ disabled: boolean;
11
+ isNewProperty: boolean;
12
+ autoUpdateId: boolean;
13
+ };
14
+
15
+ export const CommonPropertyFields = React.forwardRef<HTMLDivElement, CommonPropertyFieldsProps>(
16
+ function CommonPropertyFields({
17
+ showErrors,
18
+ disabledId,
19
+ disabled,
20
+ autoUpdateId,
21
+ isNewProperty
22
+ }, ref) {
23
+
24
+ const {
25
+ errors,
26
+ values,
27
+ setFieldValue,
28
+ setFieldTouched,
29
+ touched,
30
+ validate
31
+ } = useFormex<PropertyWithId>();
32
+
33
+ const name = "name";
34
+ const nameError = showErrors && getIn(errors, name);
35
+
36
+ const id = "id";
37
+ const idError = showErrors && getIn(errors, id);
38
+
39
+ const description = "description";
40
+ const descriptionError = showErrors && getIn(errors, description);
41
+
42
+ return (
43
+ <div className={"flex flex-col gap-2 col-span-12"}>
44
+
45
+ <div>
46
+ <Field
47
+ name={name}
48
+ inputRef={ref}
49
+ as={DebouncedTextField}
50
+ value={values[name]}
51
+ onChange={(e: any) => {
52
+ const newNameValue = e.target.value;
53
+ const idTouched = getIn(touched, id);
54
+ if (!idTouched && autoUpdateId) {
55
+ setFieldValue(id, newNameValue ? toSnakeCase(newNameValue) : "", false)
56
+ }
57
+ setFieldValue(name, newNameValue, true);
58
+ setFieldTouched(name, true);
59
+ }}
60
+ style={{ fontSize: 20 }}
61
+ placeholder={"Field name"}
62
+ required
63
+ disabled={disabled}
64
+ error={Boolean(nameError)}/>
65
+
66
+ <FieldCaption error={Boolean(nameError)}>
67
+ {nameError}
68
+ </FieldCaption>
69
+ </div>
70
+
71
+ <div>
72
+ <Field
73
+ name={id}
74
+ as={DebouncedTextField}
75
+ label={"ID"}
76
+ value={values[id]}
77
+ onChange={(e: any) => {
78
+ const newIdValue = e.target.value;
79
+ const nameTouched = getIn(touched, name);
80
+ if (!nameTouched && autoUpdateId) {
81
+ setFieldValue(name, newIdValue ? unslugify(newIdValue) : "")
82
+ }
83
+ setFieldValue(id, newIdValue, true);
84
+ setFieldTouched(id, true);
85
+ }}
86
+ disabled={disabledId || disabled}
87
+ required
88
+ size="small"
89
+ error={Boolean(idError)}/>
90
+ <FieldCaption error={Boolean(idError)}>
91
+ {idError}
92
+ </FieldCaption>
93
+ </div>
94
+
95
+ <div>
96
+ <Field name={description}
97
+ as={DebouncedTextField}
98
+ label={"Description"}
99
+ disabled={disabled}
100
+ error={Boolean(descriptionError)}/>
101
+ <FieldCaption error={Boolean(descriptionError)}>
102
+ {descriptionError}
103
+ </FieldCaption>
104
+ </div>
105
+
106
+ </div>
107
+ );
108
+
109
+ }
110
+ );