@firecms/collection_editor 3.0.0-canary.28 → 3.0.0-canary.280
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +114 -21
- package/README.md +165 -1
- package/dist/ConfigControllerProvider.d.ts +1 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +10792 -4791
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +11458 -3
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +3 -2
- package/dist/types/collection_inference.d.ts +4 -1
- package/dist/types/config_controller.d.ts +3 -1
- package/dist/types/config_permissions.d.ts +2 -2
- package/dist/types/persisted_collection.d.ts +1 -1
- package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
- package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
- package/dist/ui/EditorEntityAction.d.ts +2 -0
- package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +3 -1
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +3 -2
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +2 -2
- package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +2 -2
- package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +4 -0
- package/dist/ui/collection_editor/EntityActionsSelectDialog.d.ts +4 -0
- package/dist/ui/collection_editor/LayoutModeSwitch.d.ts +5 -0
- package/dist/ui/collection_editor/PropertyEditView.d.ts +8 -0
- package/dist/ui/collection_editor/PropertyTree.d.ts +11 -12
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
- package/dist/ui/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
- package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +8 -1
- package/dist/ui/collection_editor/import/clean_import_data.d.ts +1 -1
- package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
- package/dist/ui/collection_editor/properties/ReferencePropertyField.d.ts +2 -1
- package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
- package/dist/useCollectionEditorPlugin.d.ts +8 -11
- package/dist/utils/collections.d.ts +6 -0
- package/package.json +25 -37
- package/src/ConfigControllerProvider.tsx +64 -66
- package/src/index.ts +1 -0
- package/src/types/collection_editor_controller.tsx +6 -5
- package/src/types/collection_inference.ts +4 -1
- package/src/types/config_controller.tsx +4 -1
- package/src/types/config_permissions.ts +1 -1
- package/src/types/persisted_collection.ts +2 -3
- package/src/ui/CollectionViewHeaderAction.tsx +10 -5
- package/src/ui/EditorCollectionAction.tsx +12 -70
- package/src/ui/EditorCollectionActionStart.tsx +87 -0
- package/src/ui/EditorEntityAction.tsx +51 -0
- package/src/ui/HomePageEditorCollectionAction.tsx +21 -14
- package/src/ui/NewCollectionButton.tsx +1 -1
- package/src/ui/NewCollectionCard.tsx +3 -3
- package/src/ui/PropertyAddColumnComponent.tsx +11 -6
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +157 -50
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +119 -39
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +24 -33
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +46 -41
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +163 -0
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +41 -0
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +11 -7
- package/src/ui/collection_editor/EnumForm.tsx +11 -7
- package/src/ui/collection_editor/GetCodeDialog.tsx +60 -28
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +54 -0
- package/src/ui/collection_editor/PropertyEditView.tsx +266 -79
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +8 -10
- package/src/ui/collection_editor/PropertyTree.tsx +184 -138
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +26 -19
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +9 -7
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +41 -9
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +43 -10
- package/src/ui/collection_editor/import/clean_import_data.ts +1 -1
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +32 -20
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +54 -47
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -7
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +7 -3
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +34 -19
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +4 -9
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +1 -0
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +2 -0
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +2 -2
- package/src/ui/collection_editor/templates/pages_template.ts +1 -6
- package/src/ui/collection_editor/utils/strings.ts +13 -6
- package/src/ui/collection_editor/utils/supported_fields.tsx +2 -0
- package/src/ui/collection_editor/utils/update_property_for_widget.ts +37 -6
- package/src/useCollectionEditorPlugin.tsx +38 -32
- package/src/utils/collections.ts +46 -0
- package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
- package/dist/ui/collection_editor/PropertySelectItem.d.ts +0 -8
- package/src/ui/RootCollectionSuggestions.tsx +0 -63
- package/src/ui/collection_editor/PropertySelectItem.tsx +0 -32
|
@@ -3,9 +3,8 @@ import equal from "react-fast-compare"
|
|
|
3
3
|
|
|
4
4
|
import { Formex, FormexController, getIn, useCreateFormex } from "@firecms/formex";
|
|
5
5
|
import {
|
|
6
|
+
ConfirmationDialog,
|
|
6
7
|
DEFAULT_FIELD_CONFIGS,
|
|
7
|
-
DeleteConfirmationDialog,
|
|
8
|
-
PropertyConfigId,
|
|
9
8
|
getFieldConfig,
|
|
10
9
|
getFieldId,
|
|
11
10
|
isPropertyBuilder,
|
|
@@ -14,18 +13,25 @@ import {
|
|
|
14
13
|
Property,
|
|
15
14
|
PropertyConfig,
|
|
16
15
|
PropertyConfigBadge,
|
|
16
|
+
PropertyConfigId,
|
|
17
17
|
} from "@firecms/core";
|
|
18
18
|
import {
|
|
19
19
|
Button,
|
|
20
|
-
|
|
20
|
+
Card,
|
|
21
|
+
cls,
|
|
21
22
|
DeleteIcon,
|
|
22
23
|
Dialog,
|
|
23
24
|
DialogActions,
|
|
24
25
|
DialogContent,
|
|
26
|
+
DialogTitle,
|
|
27
|
+
fieldBackgroundDisabledMixin,
|
|
28
|
+
fieldBackgroundHoverMixin,
|
|
29
|
+
fieldBackgroundMixin,
|
|
25
30
|
IconButton,
|
|
26
31
|
InfoLabel,
|
|
27
|
-
|
|
28
|
-
Typography
|
|
32
|
+
Tooltip,
|
|
33
|
+
Typography,
|
|
34
|
+
WarningIcon
|
|
29
35
|
} from "@firecms/ui";
|
|
30
36
|
import { EnumPropertyField } from "./properties/EnumPropertyField";
|
|
31
37
|
import { StoragePropertyField } from "./properties/StoragePropertyField";
|
|
@@ -42,9 +48,9 @@ import { AdvancedPropertyValidation } from "./properties/advanced/AdvancedProper
|
|
|
42
48
|
import { editableProperty } from "../../utils/entities";
|
|
43
49
|
import { KeyValuePropertyField } from "./properties/KeyValuePropertyField";
|
|
44
50
|
import { updatePropertyFromWidget } from "./utils/update_property_for_widget";
|
|
45
|
-
import { PropertySelectItem } from "./PropertySelectItem";
|
|
46
51
|
import { UrlPropertyField } from "./properties/UrlPropertyField";
|
|
47
52
|
import { supportedFields } from "./utils/supported_fields";
|
|
53
|
+
import { MarkdownPropertyField } from "./properties/MarkdownPropertyField";
|
|
48
54
|
|
|
49
55
|
export type PropertyWithId = Property & {
|
|
50
56
|
id?: string
|
|
@@ -68,6 +74,7 @@ export type PropertyFormProps = {
|
|
|
68
74
|
property?: Property;
|
|
69
75
|
onPropertyChanged?: (params: OnPropertyChangedParams) => void;
|
|
70
76
|
onPropertyChangedImmediate?: boolean;
|
|
77
|
+
onDismiss?: () => void;
|
|
71
78
|
onDelete?: (id?: string, namespace?: string) => void;
|
|
72
79
|
onError?: (id: string, namespace?: string, error?: Record<string, any>) => void;
|
|
73
80
|
initialErrors?: Record<string, any>;
|
|
@@ -95,6 +102,7 @@ export const PropertyForm = React.memo(
|
|
|
95
102
|
property,
|
|
96
103
|
onPropertyChanged,
|
|
97
104
|
onPropertyChangedImmediate = true,
|
|
105
|
+
onDismiss,
|
|
98
106
|
onDelete,
|
|
99
107
|
onError,
|
|
100
108
|
initialErrors,
|
|
@@ -134,6 +142,7 @@ export const PropertyForm = React.memo(
|
|
|
134
142
|
};
|
|
135
143
|
|
|
136
144
|
const formexController = useCreateFormex<PropertyWithId>({
|
|
145
|
+
debugId: "PROPERTY_FORM",
|
|
137
146
|
initialValues: property
|
|
138
147
|
? { id: propertyKey, ...property } as PropertyWithId
|
|
139
148
|
: initialValue,
|
|
@@ -141,14 +150,16 @@ export const PropertyForm = React.memo(
|
|
|
141
150
|
validateOnChange: true,
|
|
142
151
|
validateOnInitialRender: true,
|
|
143
152
|
onSubmit: (newPropertyWithId, controller) => {
|
|
144
|
-
console.debug("onSubmit", newPropertyWithId);
|
|
145
153
|
const {
|
|
146
154
|
id,
|
|
147
155
|
...property
|
|
148
156
|
} = newPropertyWithId;
|
|
149
157
|
doOnPropertyChanged({
|
|
150
158
|
id,
|
|
151
|
-
property: {
|
|
159
|
+
property: {
|
|
160
|
+
...property,
|
|
161
|
+
editable: property.editable ?? true
|
|
162
|
+
}
|
|
152
163
|
});
|
|
153
164
|
if (!existingProperty)
|
|
154
165
|
controller.resetForm({ values: initialValue });
|
|
@@ -209,6 +220,7 @@ export const PropertyForm = React.memo(
|
|
|
209
220
|
includeIdAndTitle={includeIdAndName}
|
|
210
221
|
propertyNamespace={propertyNamespace}
|
|
211
222
|
onError={onError}
|
|
223
|
+
onDismiss={onDismiss}
|
|
212
224
|
showErrors={forceShowErrors || formexController.submitCount > 0}
|
|
213
225
|
existing={existingProperty}
|
|
214
226
|
autoUpdateId={autoUpdateId}
|
|
@@ -228,6 +240,7 @@ export const PropertyForm = React.memo(
|
|
|
228
240
|
a.includeIdAndName === b.includeIdAndName &&
|
|
229
241
|
a.autoOpenTypeSelect === b.autoOpenTypeSelect &&
|
|
230
242
|
a.autoUpdateId === b.autoUpdateId &&
|
|
243
|
+
a.existingPropertyKeys === b.existingPropertyKeys &&
|
|
231
244
|
a.existingProperty === b.existingProperty
|
|
232
245
|
);
|
|
233
246
|
|
|
@@ -261,8 +274,11 @@ export function PropertyFormDialog({
|
|
|
261
274
|
e.stopPropagation();
|
|
262
275
|
formexRef.current?.handleSubmit(e)
|
|
263
276
|
}}>
|
|
277
|
+
<DialogTitle hidden>Property edit view</DialogTitle>
|
|
264
278
|
<DialogContent>
|
|
279
|
+
|
|
265
280
|
<PropertyForm {...formProps}
|
|
281
|
+
onDismiss={onCancel}
|
|
266
282
|
onPropertyChanged={(params) => {
|
|
267
283
|
onPropertyChanged?.(params);
|
|
268
284
|
onOkClicked?.();
|
|
@@ -278,6 +294,7 @@ export function PropertyFormDialog({
|
|
|
278
294
|
|
|
279
295
|
{onCancel && <Button
|
|
280
296
|
variant={"text"}
|
|
297
|
+
color={"primary"}
|
|
281
298
|
onClick={() => {
|
|
282
299
|
onCancel();
|
|
283
300
|
formexRef.current?.resetForm();
|
|
@@ -307,6 +324,7 @@ function PropertyEditFormFields({
|
|
|
307
324
|
onPropertyChanged,
|
|
308
325
|
onDelete,
|
|
309
326
|
propertyNamespace,
|
|
327
|
+
onDismiss,
|
|
310
328
|
onError,
|
|
311
329
|
showErrors,
|
|
312
330
|
disabled,
|
|
@@ -321,6 +339,7 @@ function PropertyEditFormFields({
|
|
|
321
339
|
autoUpdateId?: boolean;
|
|
322
340
|
autoOpenTypeSelect: boolean;
|
|
323
341
|
propertyNamespace?: string;
|
|
342
|
+
onDismiss?: () => void;
|
|
324
343
|
onPropertyChanged?: (params: OnPropertyChangedParams) => void;
|
|
325
344
|
onDelete?: (id?: string, namespace?: string) => void;
|
|
326
345
|
onError?: (id: string, namespace?: string, error?: Record<string, any>) => void;
|
|
@@ -337,12 +356,6 @@ function PropertyEditFormFields({
|
|
|
337
356
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
338
357
|
const [selectedFieldConfigId, setSelectedFieldConfigId] = useState<string | undefined>(values?.dataType ? getFieldId(values) : undefined);
|
|
339
358
|
|
|
340
|
-
const allSupportedFields = Object.entries(supportedFields).concat(Object.entries(propertyConfigs));
|
|
341
|
-
|
|
342
|
-
const displayedWidgets = inArray
|
|
343
|
-
? allSupportedFields.filter(([_, propertyConfig]) => !isPropertyBuilder(propertyConfig.property) && propertyConfig.property?.dataType !== "array")
|
|
344
|
-
: allSupportedFields;
|
|
345
|
-
|
|
346
359
|
const deferredValues = useDeferredValue(values);
|
|
347
360
|
const nameFieldRef = useRef<HTMLInputElement>(null);
|
|
348
361
|
|
|
@@ -367,13 +380,13 @@ function PropertyEditFormFields({
|
|
|
367
380
|
}
|
|
368
381
|
}
|
|
369
382
|
}
|
|
370
|
-
}, [deferredValues, includeIdAndTitle,
|
|
383
|
+
}, [deferredValues, includeIdAndTitle, propertyNamespace]);
|
|
371
384
|
|
|
372
385
|
useEffect(() => {
|
|
373
386
|
if (values?.id && onError) {
|
|
374
387
|
onError(values?.id, propertyNamespace, errors);
|
|
375
388
|
}
|
|
376
|
-
}, [errors,
|
|
389
|
+
}, [errors, propertyNamespace, values?.id]);
|
|
377
390
|
|
|
378
391
|
const onWidgetSelectChanged = (newSelectedWidgetId: PropertyConfigId) => {
|
|
379
392
|
setSelectedFieldConfigId(newSelectedWidgetId);
|
|
@@ -387,7 +400,7 @@ function PropertyEditFormFields({
|
|
|
387
400
|
let childComponent;
|
|
388
401
|
if (selectedFieldConfigId === "text_field" ||
|
|
389
402
|
selectedFieldConfigId === "multiline" ||
|
|
390
|
-
selectedFieldConfigId === "
|
|
403
|
+
selectedFieldConfigId === "user_select" ||
|
|
391
404
|
selectedFieldConfigId === "email") {
|
|
392
405
|
childComponent =
|
|
393
406
|
<StringPropertyField widgetId={selectedFieldConfigId}
|
|
@@ -397,6 +410,10 @@ function PropertyEditFormFields({
|
|
|
397
410
|
childComponent =
|
|
398
411
|
<UrlPropertyField disabled={disabled}
|
|
399
412
|
showErrors={showErrors}/>;
|
|
413
|
+
} else if (selectedFieldConfigId === "markdown") {
|
|
414
|
+
childComponent =
|
|
415
|
+
<MarkdownPropertyField disabled={disabled}
|
|
416
|
+
showErrors={showErrors}/>;
|
|
400
417
|
} else if (selectedFieldConfigId === "select" ||
|
|
401
418
|
selectedFieldConfigId === "number_select") {
|
|
402
419
|
childComponent = <EnumPropertyField
|
|
@@ -445,6 +462,13 @@ function PropertyEditFormFields({
|
|
|
445
462
|
existing={existing}
|
|
446
463
|
multiple={false}
|
|
447
464
|
disabled={disabled}/>;
|
|
465
|
+
} else if (selectedFieldConfigId === "reference_as_string") {
|
|
466
|
+
childComponent =
|
|
467
|
+
<ReferencePropertyField showErrors={showErrors}
|
|
468
|
+
existing={existing}
|
|
469
|
+
asString={true}
|
|
470
|
+
multiple={false}
|
|
471
|
+
disabled={disabled}/>;
|
|
448
472
|
} else if (selectedFieldConfigId === "date_time") {
|
|
449
473
|
childComponent = <DateTimePropertyField disabled={disabled}/>;
|
|
450
474
|
} else if (selectedFieldConfigId === "multi_references") {
|
|
@@ -481,62 +505,22 @@ function PropertyEditFormFields({
|
|
|
481
505
|
|
|
482
506
|
<div className="flex mt-2 justify-between">
|
|
483
507
|
<div className={"w-full flex flex-col gap-2"}>
|
|
484
|
-
<
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
placeholder={"Select a property widget"}
|
|
508
|
+
<WidgetSelectView
|
|
509
|
+
initialProperty={values}
|
|
510
|
+
value={selectedFieldConfigId as PropertyConfigId}
|
|
511
|
+
onValueChange={(value) => onWidgetSelectChanged(value as PropertyConfigId)}
|
|
489
512
|
open={selectOpen}
|
|
490
|
-
onOpenChange={
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
renderValue={(value) => {
|
|
494
|
-
if (!value) {
|
|
495
|
-
return <em>Select a property
|
|
496
|
-
widget</em>;
|
|
513
|
+
onOpenChange={(open, hasValue) => {
|
|
514
|
+
if (!hasValue) {
|
|
515
|
+
onDismiss?.();
|
|
497
516
|
}
|
|
498
|
-
|
|
499
|
-
const propertyConfig = DEFAULT_FIELD_CONFIGS[key] ?? propertyConfigs[key];
|
|
500
|
-
const baseProperty = propertyConfig.property;
|
|
501
|
-
const baseFieldConfig = baseProperty && !isPropertyBuilder(baseProperty) ? getFieldConfig(baseProperty, propertyConfigs) : undefined;
|
|
502
|
-
const optionDisabled = isPropertyBuilder(baseProperty) || (existing && baseProperty.dataType !== values?.dataType);
|
|
503
|
-
const computedFieldConfig = baseFieldConfig ? mergeDeep(baseFieldConfig, propertyConfig) : propertyConfig;
|
|
504
|
-
return <div
|
|
505
|
-
onClick={(e) => {
|
|
506
|
-
if (optionDisabled) {
|
|
507
|
-
e.stopPropagation();
|
|
508
|
-
e.preventDefault();
|
|
509
|
-
}
|
|
510
|
-
}}
|
|
511
|
-
className={cn(
|
|
512
|
-
"flex items-center",
|
|
513
|
-
optionDisabled ? "w-full pointer-events-none opacity-50" : "")}>
|
|
514
|
-
<div className={"mr-8"}>
|
|
515
|
-
<PropertyConfigBadge propertyConfig={computedFieldConfig}/>
|
|
516
|
-
</div>
|
|
517
|
-
<div className={"flex flex-col items-start text-base text-left"}>
|
|
518
|
-
<div>{computedFieldConfig.name}</div>
|
|
519
|
-
<Typography variant={"caption"}
|
|
520
|
-
color={"disabled"}>
|
|
521
|
-
{optionDisabled ? "You can only switch to widgets that use the same data type" : computedFieldConfig.description}
|
|
522
|
-
</Typography>
|
|
523
|
-
</div>
|
|
524
|
-
</div>
|
|
517
|
+
setSelectOpen(open);
|
|
525
518
|
}}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
{
|
|
530
|
-
|
|
531
|
-
const optionDisabled = existing && !isPropertyBuilder(baseProperty) && baseProperty.dataType !== values?.dataType;
|
|
532
|
-
return <PropertySelectItem
|
|
533
|
-
key={key}
|
|
534
|
-
value={key}
|
|
535
|
-
optionDisabled={optionDisabled}
|
|
536
|
-
propertyConfig={propertyConfig}
|
|
537
|
-
existing={existing}/>;
|
|
538
|
-
})}
|
|
539
|
-
</Select>
|
|
519
|
+
disabled={disabled}
|
|
520
|
+
showError={Boolean(selectedWidgetError)}
|
|
521
|
+
existing={existing}
|
|
522
|
+
propertyConfigs={propertyConfigs}
|
|
523
|
+
inArray={inArray}/>
|
|
540
524
|
|
|
541
525
|
{selectedWidgetError &&
|
|
542
526
|
<Typography variant="caption"
|
|
@@ -575,15 +559,15 @@ function PropertyEditFormFields({
|
|
|
575
559
|
</div>
|
|
576
560
|
|
|
577
561
|
{onDelete &&
|
|
578
|
-
<
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
562
|
+
<ConfirmationDialog open={deleteDialogOpen}
|
|
563
|
+
onAccept={() => onDelete(values?.id, propertyNamespace)}
|
|
564
|
+
onCancel={() => setDeleteDialogOpen(false)}
|
|
565
|
+
title={<div>Delete this property?</div>}
|
|
566
|
+
body={
|
|
567
|
+
<div> This will <b>not delete any
|
|
568
|
+
data</b>, only modify the
|
|
569
|
+
collection.</div>
|
|
570
|
+
}/>}
|
|
587
571
|
|
|
588
572
|
</>
|
|
589
573
|
);
|
|
@@ -613,3 +597,206 @@ function validateName(value: string) {
|
|
|
613
597
|
}
|
|
614
598
|
return error;
|
|
615
599
|
}
|
|
600
|
+
|
|
601
|
+
const WIDGET_TYPE_MAP: Record<PropertyConfigId, string> = {
|
|
602
|
+
text_field: "Text",
|
|
603
|
+
multiline: "Text",
|
|
604
|
+
markdown: "Text",
|
|
605
|
+
url: "Text",
|
|
606
|
+
email: "Text",
|
|
607
|
+
switch: "Boolean",
|
|
608
|
+
select: "Select",
|
|
609
|
+
multi_select: "Select",
|
|
610
|
+
user_select: "Select",
|
|
611
|
+
number_input: "Number",
|
|
612
|
+
number_select: "Select",
|
|
613
|
+
multi_number_select: "Select",
|
|
614
|
+
file_upload: "File",
|
|
615
|
+
multi_file_upload: "File",
|
|
616
|
+
reference: "Reference",
|
|
617
|
+
reference_as_string: "Text",
|
|
618
|
+
multi_references: "Reference",
|
|
619
|
+
date_time: "Date",
|
|
620
|
+
group: "Group",
|
|
621
|
+
key_value: "Group",
|
|
622
|
+
repeat: "Array",
|
|
623
|
+
custom_array: "Array",
|
|
624
|
+
block: "Group"
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
function WidgetSelectView({
|
|
628
|
+
initialProperty,
|
|
629
|
+
value,
|
|
630
|
+
onValueChange,
|
|
631
|
+
open,
|
|
632
|
+
onOpenChange,
|
|
633
|
+
disabled,
|
|
634
|
+
showError,
|
|
635
|
+
existing,
|
|
636
|
+
propertyConfigs,
|
|
637
|
+
inArray
|
|
638
|
+
}: {
|
|
639
|
+
initialProperty?: PropertyWithId,
|
|
640
|
+
value?: PropertyConfigId,
|
|
641
|
+
onValueChange: (value: string) => void,
|
|
642
|
+
showError: boolean,
|
|
643
|
+
open: boolean,
|
|
644
|
+
onOpenChange: (open: boolean, hasValue: boolean) => void,
|
|
645
|
+
disabled: boolean,
|
|
646
|
+
existing: boolean,
|
|
647
|
+
propertyConfigs: Record<string, PropertyConfig>,
|
|
648
|
+
inArray?: boolean
|
|
649
|
+
}) {
|
|
650
|
+
|
|
651
|
+
const allSupportedFields = Object.entries(supportedFields).concat(Object.entries(propertyConfigs));
|
|
652
|
+
|
|
653
|
+
const displayedWidgets = (inArray
|
|
654
|
+
? allSupportedFields.filter(([_, propertyConfig]) => !isPropertyBuilder(propertyConfig.property) && propertyConfig.property?.dataType !== "array")
|
|
655
|
+
: allSupportedFields)
|
|
656
|
+
.map(([key, propertyConfig]) => ({
|
|
657
|
+
[key]: propertyConfig
|
|
658
|
+
}))
|
|
659
|
+
.reduce((a, b) => {
|
|
660
|
+
return {
|
|
661
|
+
...a,
|
|
662
|
+
...b
|
|
663
|
+
}
|
|
664
|
+
}, {});
|
|
665
|
+
|
|
666
|
+
const key = value;
|
|
667
|
+
const propertyConfig = key ? (DEFAULT_FIELD_CONFIGS[key] ?? propertyConfigs[key]) : undefined;
|
|
668
|
+
const baseProperty = propertyConfig?.property;
|
|
669
|
+
const baseFieldConfig = baseProperty && !isPropertyBuilder(baseProperty) ? getFieldConfig(baseProperty, propertyConfigs) : undefined;
|
|
670
|
+
const computedFieldConfig = baseFieldConfig && propertyConfig ? mergeDeep(baseFieldConfig, propertyConfig) : propertyConfig;
|
|
671
|
+
|
|
672
|
+
const groups: string[] = [...new Set(Object.keys(displayedWidgets).map(key => {
|
|
673
|
+
const group = WIDGET_TYPE_MAP[key as PropertyConfigId];
|
|
674
|
+
if (group) {
|
|
675
|
+
return group;
|
|
676
|
+
}
|
|
677
|
+
return "Custom/Other"
|
|
678
|
+
}))];
|
|
679
|
+
|
|
680
|
+
return <>
|
|
681
|
+
<div
|
|
682
|
+
onClick={() => {
|
|
683
|
+
if (!disabled) {
|
|
684
|
+
onOpenChange(!open, Boolean(value));
|
|
685
|
+
}
|
|
686
|
+
}}
|
|
687
|
+
className={cls(
|
|
688
|
+
"select-none rounded-md text-sm p-4",
|
|
689
|
+
fieldBackgroundMixin,
|
|
690
|
+
disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
|
|
691
|
+
"relative flex items-center",
|
|
692
|
+
)}>
|
|
693
|
+
{!value && <em>Select a property widget</em>}
|
|
694
|
+
{value && computedFieldConfig && <div
|
|
695
|
+
className={cls(
|
|
696
|
+
"flex items-center")}>
|
|
697
|
+
<div className={"mr-8"}>
|
|
698
|
+
<PropertyConfigBadge propertyConfig={computedFieldConfig}/>
|
|
699
|
+
</div>
|
|
700
|
+
<div className={"flex flex-col items-start text-base text-left"}>
|
|
701
|
+
<div>{computedFieldConfig.name}</div>
|
|
702
|
+
<Typography variant={"caption"}
|
|
703
|
+
color={"secondary"}>
|
|
704
|
+
{computedFieldConfig.description}
|
|
705
|
+
</Typography>
|
|
706
|
+
</div>
|
|
707
|
+
</div>}
|
|
708
|
+
</div>
|
|
709
|
+
<Dialog open={open}
|
|
710
|
+
onOpenChange={(open) => onOpenChange(open, Boolean(value))}
|
|
711
|
+
maxWidth={"4xl"}>
|
|
712
|
+
<DialogTitle>
|
|
713
|
+
Select a property widget
|
|
714
|
+
</DialogTitle>
|
|
715
|
+
<DialogContent>
|
|
716
|
+
<div>
|
|
717
|
+
{groups.map(group => {
|
|
718
|
+
return <div key={group} className={"mt-4"}>
|
|
719
|
+
<Typography variant={"label"}>{group}</Typography>
|
|
720
|
+
<div className={"grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2 mt-4"}>
|
|
721
|
+
{Object.entries(displayedWidgets).map(([key, propertyConfig]) => {
|
|
722
|
+
const groupKey = WIDGET_TYPE_MAP[key as PropertyConfigId];
|
|
723
|
+
if (groupKey === group) {
|
|
724
|
+
return <WidgetSelectViewItem
|
|
725
|
+
key={key}
|
|
726
|
+
initialProperty={initialProperty}
|
|
727
|
+
onClick={() => {
|
|
728
|
+
onValueChange(key);
|
|
729
|
+
onOpenChange(false, true);
|
|
730
|
+
}}
|
|
731
|
+
propertyConfig={propertyConfig}
|
|
732
|
+
existing={existing}/>;
|
|
733
|
+
}
|
|
734
|
+
return null;
|
|
735
|
+
})}
|
|
736
|
+
</div>
|
|
737
|
+
</div>;
|
|
738
|
+
})}
|
|
739
|
+
{/*{displayedWidgets.map(([key, propertyConfig]) => {*/}
|
|
740
|
+
{/* return <WidgetSelectViewItem*/}
|
|
741
|
+
{/* key={key}*/}
|
|
742
|
+
{/* initialProperty={initialProperty}*/}
|
|
743
|
+
{/* onClick={() => {*/}
|
|
744
|
+
{/* onValueChange(key);*/}
|
|
745
|
+
{/* onOpenChange(false);*/}
|
|
746
|
+
{/* }}*/}
|
|
747
|
+
{/* propertyConfig={propertyConfig}*/}
|
|
748
|
+
{/* existing={existing}/>;*/}
|
|
749
|
+
{/*})}*/}
|
|
750
|
+
</div>
|
|
751
|
+
</DialogContent>
|
|
752
|
+
</Dialog>
|
|
753
|
+
</>;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export interface PropertySelectItemProps {
|
|
757
|
+
onClick?: () => void;
|
|
758
|
+
initialProperty?: PropertyWithId;
|
|
759
|
+
propertyConfig: PropertyConfig;
|
|
760
|
+
existing: boolean;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
export function WidgetSelectViewItem({
|
|
764
|
+
onClick,
|
|
765
|
+
initialProperty,
|
|
766
|
+
// optionDisabled,
|
|
767
|
+
propertyConfig,
|
|
768
|
+
existing
|
|
769
|
+
}: PropertySelectItemProps) {
|
|
770
|
+
const baseProperty = propertyConfig.property;
|
|
771
|
+
const shouldWarnChangingDataType = existing && !isPropertyBuilder(baseProperty) && baseProperty.dataType !== initialProperty?.dataType;
|
|
772
|
+
|
|
773
|
+
return <Card
|
|
774
|
+
onClick={onClick}
|
|
775
|
+
className={"flex flex-row items-center px-4 py-2 m-1"}>
|
|
776
|
+
<div
|
|
777
|
+
className={cls(
|
|
778
|
+
"flex flex-row items-center text-base min-h-[48px]",
|
|
779
|
+
)}>
|
|
780
|
+
<div className={"mr-8"}>
|
|
781
|
+
<PropertyConfigBadge propertyConfig={propertyConfig} disabled={shouldWarnChangingDataType}/>
|
|
782
|
+
</div>
|
|
783
|
+
<div>
|
|
784
|
+
<div className={"flex flex-row gap-2 items-center"}>
|
|
785
|
+
{shouldWarnChangingDataType && <Tooltip
|
|
786
|
+
title={"This widget uses a different data type than the initially selected widget. This can cause errors with existing data."}>
|
|
787
|
+
<WarningIcon size="smallest" className={"w-4"}/>
|
|
788
|
+
</Tooltip>}
|
|
789
|
+
<Typography
|
|
790
|
+
color={shouldWarnChangingDataType ? "secondary" : undefined}>{propertyConfig.name}</Typography>
|
|
791
|
+
</div>
|
|
792
|
+
|
|
793
|
+
<Typography variant={"caption"}
|
|
794
|
+
color={"secondary"}
|
|
795
|
+
className={"max-w-sm"}>
|
|
796
|
+
{propertyConfig.description}
|
|
797
|
+
</Typography>
|
|
798
|
+
|
|
799
|
+
</div>
|
|
800
|
+
</div>
|
|
801
|
+
</Card>
|
|
802
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ErrorBoundary,
|
|
3
|
-
PropertyConfigBadge,
|
|
4
3
|
getFieldConfig,
|
|
5
4
|
isPropertyBuilder,
|
|
6
5
|
Property,
|
|
6
|
+
PropertyConfigBadge,
|
|
7
7
|
PropertyOrBuilder,
|
|
8
8
|
useCustomizationController,
|
|
9
9
|
} from "@firecms/core";
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
cardClickableMixin,
|
|
12
12
|
cardMixin,
|
|
13
13
|
cardSelectedMixin,
|
|
14
|
-
|
|
14
|
+
cls,
|
|
15
|
+
DoNotDisturbOnIcon,
|
|
15
16
|
FunctionsIcon,
|
|
16
17
|
Paper,
|
|
17
|
-
RemoveCircleIcon,
|
|
18
18
|
Typography,
|
|
19
19
|
} from "@firecms/ui";
|
|
20
20
|
|
|
@@ -45,9 +45,6 @@ export function PropertyFieldPreview({
|
|
|
45
45
|
? "border-red-500 dark:border-red-500 border-opacity-100 dark:border-opacity-100 ring-0 dark:ring-0"
|
|
46
46
|
: (selected ? "border-primary" : "border-transparent");
|
|
47
47
|
|
|
48
|
-
if(hasError)
|
|
49
|
-
console.log("PropertyFieldPreview", property)
|
|
50
|
-
|
|
51
48
|
return <ErrorBoundary>
|
|
52
49
|
<div
|
|
53
50
|
onClick={onClick}
|
|
@@ -56,7 +53,8 @@ export function PropertyFieldPreview({
|
|
|
56
53
|
<PropertyConfigBadge propertyConfig={propertyConfig}/>
|
|
57
54
|
</div>
|
|
58
55
|
<Paper
|
|
59
|
-
className={
|
|
56
|
+
className={cls(
|
|
57
|
+
"m-1",
|
|
60
58
|
"border",
|
|
61
59
|
"pl-2 w-full flex flex-row gap-4 items-center",
|
|
62
60
|
cardMixin,
|
|
@@ -133,13 +131,13 @@ export function NonEditablePropertyPreview({
|
|
|
133
131
|
<div className={"relative m-4"}>
|
|
134
132
|
{propertyConfig && <PropertyConfigBadge propertyConfig={propertyConfig}/>}
|
|
135
133
|
{!propertyConfig && <div
|
|
136
|
-
className={"h-8 w-8 p-1 rounded-full shadow text-white bg-
|
|
134
|
+
className={"h-8 w-8 p-1 rounded-full shadow text-white bg-surface-500"}>
|
|
137
135
|
<FunctionsIcon color={"inherit"} size={"medium"}/>
|
|
138
136
|
</div>}
|
|
139
|
-
<
|
|
137
|
+
<DoNotDisturbOnIcon color={"disabled"} size={"small"} className={"absolute -right-2 -top-2"}/>
|
|
140
138
|
</div>
|
|
141
139
|
<Paper
|
|
142
|
-
className={
|
|
140
|
+
className={cls(
|
|
143
141
|
"pl-2 w-full flex flex-row gap-4 items-center",
|
|
144
142
|
cardMixin,
|
|
145
143
|
onClick ? cardClickableMixin : "",
|