@firecms/collection_editor 3.0.0-canary.20 → 3.0.0-canary.201
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/dist/ConfigControllerProvider.d.ts +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +10061 -4770
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +10751 -3
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +4 -2
- package/dist/types/collection_inference.d.ts +1 -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/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 +1 -1
- package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -1
- 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 +9 -9
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
- package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +7 -0
- package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
- 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 +24 -35
- package/src/ConfigControllerProvider.tsx +67 -64
- package/src/index.ts +1 -0
- package/src/types/collection_editor_controller.tsx +7 -4
- package/src/types/collection_inference.ts +1 -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 +10 -63
- package/src/ui/EditorCollectionActionStart.tsx +88 -0
- package/src/ui/HomePageEditorCollectionAction.tsx +19 -13
- 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 +88 -11
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +101 -34
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +8 -7
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +39 -36
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +6 -5
- package/src/ui/collection_editor/EnumForm.tsx +10 -6
- package/src/ui/collection_editor/GetCodeDialog.tsx +56 -26
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +54 -0
- package/src/ui/collection_editor/PropertyEditView.tsx +257 -79
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +7 -10
- package/src/ui/collection_editor/PropertyTree.tsx +9 -7
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +26 -19
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +3 -5
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +26 -9
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +42 -9
- 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 +7 -6
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +2 -0
- 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 +1 -10
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +1 -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/useCollectionEditorPlugin.tsx +33 -32
- package/src/utils/collections.ts +36 -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,29 +3,36 @@ 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,
|
|
10
|
+
isEmptyObject,
|
|
11
11
|
isPropertyBuilder,
|
|
12
12
|
isValidRegExp,
|
|
13
13
|
mergeDeep,
|
|
14
14
|
Property,
|
|
15
15
|
PropertyConfig,
|
|
16
16
|
PropertyConfigBadge,
|
|
17
|
+
PropertyConfigId,
|
|
17
18
|
} from "@firecms/core";
|
|
18
19
|
import {
|
|
19
20
|
Button,
|
|
20
|
-
|
|
21
|
+
Card,
|
|
22
|
+
cls,
|
|
21
23
|
DeleteIcon,
|
|
22
24
|
Dialog,
|
|
23
25
|
DialogActions,
|
|
24
26
|
DialogContent,
|
|
27
|
+
DialogTitle,
|
|
28
|
+
fieldBackgroundDisabledMixin,
|
|
29
|
+
fieldBackgroundHoverMixin,
|
|
30
|
+
fieldBackgroundMixin,
|
|
25
31
|
IconButton,
|
|
26
32
|
InfoLabel,
|
|
27
|
-
|
|
28
|
-
Typography
|
|
33
|
+
Tooltip,
|
|
34
|
+
Typography,
|
|
35
|
+
WarningIcon
|
|
29
36
|
} from "@firecms/ui";
|
|
30
37
|
import { EnumPropertyField } from "./properties/EnumPropertyField";
|
|
31
38
|
import { StoragePropertyField } from "./properties/StoragePropertyField";
|
|
@@ -42,9 +49,9 @@ import { AdvancedPropertyValidation } from "./properties/advanced/AdvancedProper
|
|
|
42
49
|
import { editableProperty } from "../../utils/entities";
|
|
43
50
|
import { KeyValuePropertyField } from "./properties/KeyValuePropertyField";
|
|
44
51
|
import { updatePropertyFromWidget } from "./utils/update_property_for_widget";
|
|
45
|
-
import { PropertySelectItem } from "./PropertySelectItem";
|
|
46
52
|
import { UrlPropertyField } from "./properties/UrlPropertyField";
|
|
47
53
|
import { supportedFields } from "./utils/supported_fields";
|
|
54
|
+
import { MarkdownPropertyField } from "./properties/MarkdownPropertyField";
|
|
48
55
|
|
|
49
56
|
export type PropertyWithId = Property & {
|
|
50
57
|
id?: string
|
|
@@ -68,6 +75,7 @@ export type PropertyFormProps = {
|
|
|
68
75
|
property?: Property;
|
|
69
76
|
onPropertyChanged?: (params: OnPropertyChangedParams) => void;
|
|
70
77
|
onPropertyChangedImmediate?: boolean;
|
|
78
|
+
onDismiss?: () => void;
|
|
71
79
|
onDelete?: (id?: string, namespace?: string) => void;
|
|
72
80
|
onError?: (id: string, namespace?: string, error?: Record<string, any>) => void;
|
|
73
81
|
initialErrors?: Record<string, any>;
|
|
@@ -95,6 +103,7 @@ export const PropertyForm = React.memo(
|
|
|
95
103
|
property,
|
|
96
104
|
onPropertyChanged,
|
|
97
105
|
onPropertyChangedImmediate = true,
|
|
106
|
+
onDismiss,
|
|
98
107
|
onDelete,
|
|
99
108
|
onError,
|
|
100
109
|
initialErrors,
|
|
@@ -134,6 +143,7 @@ export const PropertyForm = React.memo(
|
|
|
134
143
|
};
|
|
135
144
|
|
|
136
145
|
const formexController = useCreateFormex<PropertyWithId>({
|
|
146
|
+
debugId: "PROPERTY_FORM",
|
|
137
147
|
initialValues: property
|
|
138
148
|
? { id: propertyKey, ...property } as PropertyWithId
|
|
139
149
|
: initialValue,
|
|
@@ -148,7 +158,10 @@ export const PropertyForm = React.memo(
|
|
|
148
158
|
} = newPropertyWithId;
|
|
149
159
|
doOnPropertyChanged({
|
|
150
160
|
id,
|
|
151
|
-
property: {
|
|
161
|
+
property: {
|
|
162
|
+
...property,
|
|
163
|
+
editable: property.editable ?? true
|
|
164
|
+
}
|
|
152
165
|
});
|
|
153
166
|
if (!existingProperty)
|
|
154
167
|
controller.resetForm({ values: initialValue });
|
|
@@ -209,6 +222,7 @@ export const PropertyForm = React.memo(
|
|
|
209
222
|
includeIdAndTitle={includeIdAndName}
|
|
210
223
|
propertyNamespace={propertyNamespace}
|
|
211
224
|
onError={onError}
|
|
225
|
+
onDismiss={onDismiss}
|
|
212
226
|
showErrors={forceShowErrors || formexController.submitCount > 0}
|
|
213
227
|
existing={existingProperty}
|
|
214
228
|
autoUpdateId={autoUpdateId}
|
|
@@ -228,6 +242,7 @@ export const PropertyForm = React.memo(
|
|
|
228
242
|
a.includeIdAndName === b.includeIdAndName &&
|
|
229
243
|
a.autoOpenTypeSelect === b.autoOpenTypeSelect &&
|
|
230
244
|
a.autoUpdateId === b.autoUpdateId &&
|
|
245
|
+
a.existingPropertyKeys === b.existingPropertyKeys &&
|
|
231
246
|
a.existingProperty === b.existingProperty
|
|
232
247
|
);
|
|
233
248
|
|
|
@@ -261,8 +276,11 @@ export function PropertyFormDialog({
|
|
|
261
276
|
e.stopPropagation();
|
|
262
277
|
formexRef.current?.handleSubmit(e)
|
|
263
278
|
}}>
|
|
279
|
+
<DialogTitle hidden>Property edit view</DialogTitle>
|
|
264
280
|
<DialogContent>
|
|
281
|
+
|
|
265
282
|
<PropertyForm {...formProps}
|
|
283
|
+
onDismiss={onCancel}
|
|
266
284
|
onPropertyChanged={(params) => {
|
|
267
285
|
onPropertyChanged?.(params);
|
|
268
286
|
onOkClicked?.();
|
|
@@ -307,6 +325,7 @@ function PropertyEditFormFields({
|
|
|
307
325
|
onPropertyChanged,
|
|
308
326
|
onDelete,
|
|
309
327
|
propertyNamespace,
|
|
328
|
+
onDismiss,
|
|
310
329
|
onError,
|
|
311
330
|
showErrors,
|
|
312
331
|
disabled,
|
|
@@ -321,6 +340,7 @@ function PropertyEditFormFields({
|
|
|
321
340
|
autoUpdateId?: boolean;
|
|
322
341
|
autoOpenTypeSelect: boolean;
|
|
323
342
|
propertyNamespace?: string;
|
|
343
|
+
onDismiss?: () => void;
|
|
324
344
|
onPropertyChanged?: (params: OnPropertyChangedParams) => void;
|
|
325
345
|
onDelete?: (id?: string, namespace?: string) => void;
|
|
326
346
|
onError?: (id: string, namespace?: string, error?: Record<string, any>) => void;
|
|
@@ -337,12 +357,6 @@ function PropertyEditFormFields({
|
|
|
337
357
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
338
358
|
const [selectedFieldConfigId, setSelectedFieldConfigId] = useState<string | undefined>(values?.dataType ? getFieldId(values) : undefined);
|
|
339
359
|
|
|
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
360
|
const deferredValues = useDeferredValue(values);
|
|
347
361
|
const nameFieldRef = useRef<HTMLInputElement>(null);
|
|
348
362
|
|
|
@@ -367,13 +381,13 @@ function PropertyEditFormFields({
|
|
|
367
381
|
}
|
|
368
382
|
}
|
|
369
383
|
}
|
|
370
|
-
}, [deferredValues, includeIdAndTitle,
|
|
384
|
+
}, [deferredValues, includeIdAndTitle, propertyNamespace]);
|
|
371
385
|
|
|
372
386
|
useEffect(() => {
|
|
373
|
-
if (values?.id && onError) {
|
|
387
|
+
if (values?.id && onError && !isEmptyObject(errors)) {
|
|
374
388
|
onError(values?.id, propertyNamespace, errors);
|
|
375
389
|
}
|
|
376
|
-
}, [errors,
|
|
390
|
+
}, [errors, propertyNamespace, values?.id]);
|
|
377
391
|
|
|
378
392
|
const onWidgetSelectChanged = (newSelectedWidgetId: PropertyConfigId) => {
|
|
379
393
|
setSelectedFieldConfigId(newSelectedWidgetId);
|
|
@@ -387,7 +401,6 @@ function PropertyEditFormFields({
|
|
|
387
401
|
let childComponent;
|
|
388
402
|
if (selectedFieldConfigId === "text_field" ||
|
|
389
403
|
selectedFieldConfigId === "multiline" ||
|
|
390
|
-
selectedFieldConfigId === "markdown" ||
|
|
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
|
|
@@ -481,62 +498,22 @@ function PropertyEditFormFields({
|
|
|
481
498
|
|
|
482
499
|
<div className="flex mt-2 justify-between">
|
|
483
500
|
<div className={"w-full flex flex-col gap-2"}>
|
|
484
|
-
<
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
placeholder={"Select a property widget"}
|
|
501
|
+
<WidgetSelectView
|
|
502
|
+
initialProperty={values}
|
|
503
|
+
value={selectedFieldConfigId as PropertyConfigId}
|
|
504
|
+
onValueChange={(value) => onWidgetSelectChanged(value as PropertyConfigId)}
|
|
489
505
|
open={selectOpen}
|
|
490
|
-
onOpenChange={
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
renderValue={(value) => {
|
|
494
|
-
if (!value) {
|
|
495
|
-
return <em>Select a property
|
|
496
|
-
widget</em>;
|
|
506
|
+
onOpenChange={(open, hasValue) => {
|
|
507
|
+
if (!hasValue) {
|
|
508
|
+
onDismiss?.();
|
|
497
509
|
}
|
|
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>
|
|
510
|
+
setSelectOpen(open);
|
|
525
511
|
}}
|
|
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>
|
|
512
|
+
disabled={disabled}
|
|
513
|
+
showError={Boolean(selectedWidgetError)}
|
|
514
|
+
existing={existing}
|
|
515
|
+
propertyConfigs={propertyConfigs}
|
|
516
|
+
inArray={inArray}/>
|
|
540
517
|
|
|
541
518
|
{selectedWidgetError &&
|
|
542
519
|
<Typography variant="caption"
|
|
@@ -575,15 +552,15 @@ function PropertyEditFormFields({
|
|
|
575
552
|
</div>
|
|
576
553
|
|
|
577
554
|
{onDelete &&
|
|
578
|
-
<
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
555
|
+
<ConfirmationDialog open={deleteDialogOpen}
|
|
556
|
+
onAccept={() => onDelete(values?.id, propertyNamespace)}
|
|
557
|
+
onCancel={() => setDeleteDialogOpen(false)}
|
|
558
|
+
title={<div>Delete this property?</div>}
|
|
559
|
+
body={
|
|
560
|
+
<div> This will <b>not delete any
|
|
561
|
+
data</b>, only modify the
|
|
562
|
+
collection.</div>
|
|
563
|
+
}/>}
|
|
587
564
|
|
|
588
565
|
</>
|
|
589
566
|
);
|
|
@@ -613,3 +590,204 @@ function validateName(value: string) {
|
|
|
613
590
|
}
|
|
614
591
|
return error;
|
|
615
592
|
}
|
|
593
|
+
|
|
594
|
+
const WIDGET_TYPE_MAP: Record<PropertyConfigId, string> = {
|
|
595
|
+
text_field: "Text",
|
|
596
|
+
multiline: "Text",
|
|
597
|
+
markdown: "Text",
|
|
598
|
+
url: "Text",
|
|
599
|
+
email: "Text",
|
|
600
|
+
switch: "Boolean",
|
|
601
|
+
select: "Select",
|
|
602
|
+
multi_select: "Select",
|
|
603
|
+
number_input: "Number",
|
|
604
|
+
number_select: "Select",
|
|
605
|
+
multi_number_select: "Select",
|
|
606
|
+
file_upload: "File",
|
|
607
|
+
multi_file_upload: "File",
|
|
608
|
+
reference: "Reference",
|
|
609
|
+
multi_references: "Reference",
|
|
610
|
+
date_time: "Date",
|
|
611
|
+
group: "Group",
|
|
612
|
+
key_value: "Group",
|
|
613
|
+
repeat: "Array",
|
|
614
|
+
custom_array: "Array",
|
|
615
|
+
block: "Group"
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
function WidgetSelectView({
|
|
619
|
+
initialProperty,
|
|
620
|
+
value,
|
|
621
|
+
onValueChange,
|
|
622
|
+
open,
|
|
623
|
+
onOpenChange,
|
|
624
|
+
disabled,
|
|
625
|
+
showError,
|
|
626
|
+
existing,
|
|
627
|
+
propertyConfigs,
|
|
628
|
+
inArray
|
|
629
|
+
}: {
|
|
630
|
+
initialProperty?: PropertyWithId,
|
|
631
|
+
value?: PropertyConfigId,
|
|
632
|
+
onValueChange: (value: string) => void,
|
|
633
|
+
showError: boolean,
|
|
634
|
+
open: boolean,
|
|
635
|
+
onOpenChange: (open: boolean, hasValue: boolean) => void,
|
|
636
|
+
disabled: boolean,
|
|
637
|
+
existing: boolean,
|
|
638
|
+
propertyConfigs: Record<string, PropertyConfig>,
|
|
639
|
+
inArray?: boolean
|
|
640
|
+
}) {
|
|
641
|
+
|
|
642
|
+
const allSupportedFields = Object.entries(supportedFields).concat(Object.entries(propertyConfigs));
|
|
643
|
+
|
|
644
|
+
const displayedWidgets = (inArray
|
|
645
|
+
? allSupportedFields.filter(([_, propertyConfig]) => !isPropertyBuilder(propertyConfig.property) && propertyConfig.property?.dataType !== "array")
|
|
646
|
+
: allSupportedFields)
|
|
647
|
+
.map(([key, propertyConfig]) => ({
|
|
648
|
+
[key]: propertyConfig
|
|
649
|
+
}))
|
|
650
|
+
.reduce((a, b) => {
|
|
651
|
+
return {
|
|
652
|
+
...a,
|
|
653
|
+
...b
|
|
654
|
+
}
|
|
655
|
+
}, {});
|
|
656
|
+
|
|
657
|
+
const key = value;
|
|
658
|
+
const propertyConfig = key ? (DEFAULT_FIELD_CONFIGS[key] ?? propertyConfigs[key]) : undefined;
|
|
659
|
+
const baseProperty = propertyConfig?.property;
|
|
660
|
+
const baseFieldConfig = baseProperty && !isPropertyBuilder(baseProperty) ? getFieldConfig(baseProperty, propertyConfigs) : undefined;
|
|
661
|
+
const computedFieldConfig = baseFieldConfig && propertyConfig ? mergeDeep(baseFieldConfig, propertyConfig) : propertyConfig;
|
|
662
|
+
|
|
663
|
+
const groups: string[] = [...new Set(Object.keys(displayedWidgets).map(key => {
|
|
664
|
+
const group = WIDGET_TYPE_MAP[key as PropertyConfigId];
|
|
665
|
+
if (group) {
|
|
666
|
+
return group;
|
|
667
|
+
}
|
|
668
|
+
return "Custom/Other"
|
|
669
|
+
}))];
|
|
670
|
+
|
|
671
|
+
return <>
|
|
672
|
+
<div
|
|
673
|
+
onClick={() => {
|
|
674
|
+
if (!disabled) {
|
|
675
|
+
onOpenChange(!open, Boolean(value));
|
|
676
|
+
}
|
|
677
|
+
}}
|
|
678
|
+
className={cls(
|
|
679
|
+
"select-none rounded-md text-sm p-4",
|
|
680
|
+
fieldBackgroundMixin,
|
|
681
|
+
disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
|
|
682
|
+
"relative flex items-center",
|
|
683
|
+
)}>
|
|
684
|
+
{!value && <em>Select a property widget</em>}
|
|
685
|
+
{value && computedFieldConfig && <div
|
|
686
|
+
className={cls(
|
|
687
|
+
"flex items-center")}>
|
|
688
|
+
<div className={"mr-8"}>
|
|
689
|
+
<PropertyConfigBadge propertyConfig={computedFieldConfig}/>
|
|
690
|
+
</div>
|
|
691
|
+
<div className={"flex flex-col items-start text-base text-left"}>
|
|
692
|
+
<div>{computedFieldConfig.name}</div>
|
|
693
|
+
<Typography variant={"caption"}
|
|
694
|
+
color={"secondary"}>
|
|
695
|
+
{computedFieldConfig.description}
|
|
696
|
+
</Typography>
|
|
697
|
+
</div>
|
|
698
|
+
</div>}
|
|
699
|
+
</div>
|
|
700
|
+
<Dialog open={open}
|
|
701
|
+
onOpenChange={(open) => onOpenChange(open, Boolean(value))}
|
|
702
|
+
maxWidth={"4xl"}>
|
|
703
|
+
<DialogTitle>
|
|
704
|
+
Select a property widget
|
|
705
|
+
</DialogTitle>
|
|
706
|
+
<DialogContent>
|
|
707
|
+
<div>
|
|
708
|
+
{groups.map(group => {
|
|
709
|
+
return <div key={group} className={"mt-4"}>
|
|
710
|
+
<Typography variant={"label"}>{group}</Typography>
|
|
711
|
+
<div className={"grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2 mt-4"}>
|
|
712
|
+
{Object.entries(displayedWidgets).map(([key, propertyConfig]) => {
|
|
713
|
+
const groupKey = WIDGET_TYPE_MAP[key as PropertyConfigId];
|
|
714
|
+
if (groupKey === group) {
|
|
715
|
+
return <WidgetSelectViewItem
|
|
716
|
+
key={key}
|
|
717
|
+
initialProperty={initialProperty}
|
|
718
|
+
onClick={() => {
|
|
719
|
+
onValueChange(key);
|
|
720
|
+
onOpenChange(false, true);
|
|
721
|
+
}}
|
|
722
|
+
propertyConfig={propertyConfig}
|
|
723
|
+
existing={existing}/>;
|
|
724
|
+
}
|
|
725
|
+
return null;
|
|
726
|
+
})}
|
|
727
|
+
</div>
|
|
728
|
+
</div>;
|
|
729
|
+
})}
|
|
730
|
+
{/*{displayedWidgets.map(([key, propertyConfig]) => {*/}
|
|
731
|
+
{/* return <WidgetSelectViewItem*/}
|
|
732
|
+
{/* key={key}*/}
|
|
733
|
+
{/* initialProperty={initialProperty}*/}
|
|
734
|
+
{/* onClick={() => {*/}
|
|
735
|
+
{/* onValueChange(key);*/}
|
|
736
|
+
{/* onOpenChange(false);*/}
|
|
737
|
+
{/* }}*/}
|
|
738
|
+
{/* propertyConfig={propertyConfig}*/}
|
|
739
|
+
{/* existing={existing}/>;*/}
|
|
740
|
+
{/*})}*/}
|
|
741
|
+
</div>
|
|
742
|
+
</DialogContent>
|
|
743
|
+
</Dialog>
|
|
744
|
+
</>;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
export interface PropertySelectItemProps {
|
|
748
|
+
onClick?: () => void;
|
|
749
|
+
initialProperty?: PropertyWithId;
|
|
750
|
+
propertyConfig: PropertyConfig;
|
|
751
|
+
existing: boolean;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
export function WidgetSelectViewItem({
|
|
755
|
+
onClick,
|
|
756
|
+
initialProperty,
|
|
757
|
+
// optionDisabled,
|
|
758
|
+
propertyConfig,
|
|
759
|
+
existing
|
|
760
|
+
}: PropertySelectItemProps) {
|
|
761
|
+
const baseProperty = propertyConfig.property;
|
|
762
|
+
const shouldWarnChangingDataType = existing && !isPropertyBuilder(baseProperty) && baseProperty.dataType !== initialProperty?.dataType;
|
|
763
|
+
|
|
764
|
+
return <Card
|
|
765
|
+
onClick={onClick}
|
|
766
|
+
className={"flex flex-row items-center px-4 py-2"}>
|
|
767
|
+
<div
|
|
768
|
+
className={cls(
|
|
769
|
+
"flex flex-row items-center text-base min-h-[48px]",
|
|
770
|
+
)}>
|
|
771
|
+
<div className={"mr-8"}>
|
|
772
|
+
<PropertyConfigBadge propertyConfig={propertyConfig} disabled={shouldWarnChangingDataType}/>
|
|
773
|
+
</div>
|
|
774
|
+
<div>
|
|
775
|
+
<div className={"flex flex-row gap-2 items-center"}>
|
|
776
|
+
{shouldWarnChangingDataType && <Tooltip
|
|
777
|
+
title={"This widget uses a different data type than the initially selected widget. This can cause errors with existing data."}>
|
|
778
|
+
<WarningIcon size="smallest" className={"w-4"}/>
|
|
779
|
+
</Tooltip>}
|
|
780
|
+
<Typography
|
|
781
|
+
color={shouldWarnChangingDataType ? "secondary" : undefined}>{propertyConfig.name}</Typography>
|
|
782
|
+
</div>
|
|
783
|
+
|
|
784
|
+
<Typography variant={"caption"}
|
|
785
|
+
color={"secondary"}
|
|
786
|
+
className={"max-w-sm"}>
|
|
787
|
+
{propertyConfig.description}
|
|
788
|
+
</Typography>
|
|
789
|
+
|
|
790
|
+
</div>
|
|
791
|
+
</div>
|
|
792
|
+
</Card>
|
|
793
|
+
}
|
|
@@ -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,7 @@ export function PropertyFieldPreview({
|
|
|
56
53
|
<PropertyConfigBadge propertyConfig={propertyConfig}/>
|
|
57
54
|
</div>
|
|
58
55
|
<Paper
|
|
59
|
-
className={
|
|
56
|
+
className={cls(
|
|
60
57
|
"border",
|
|
61
58
|
"pl-2 w-full flex flex-row gap-4 items-center",
|
|
62
59
|
cardMixin,
|
|
@@ -133,13 +130,13 @@ export function NonEditablePropertyPreview({
|
|
|
133
130
|
<div className={"relative m-4"}>
|
|
134
131
|
{propertyConfig && <PropertyConfigBadge propertyConfig={propertyConfig}/>}
|
|
135
132
|
{!propertyConfig && <div
|
|
136
|
-
className={"h-8 w-8 p-1 rounded-full shadow text-white bg-
|
|
133
|
+
className={"h-8 w-8 p-1 rounded-full shadow text-white bg-surface-500"}>
|
|
137
134
|
<FunctionsIcon color={"inherit"} size={"medium"}/>
|
|
138
135
|
</div>}
|
|
139
|
-
<
|
|
136
|
+
<DoNotDisturbOnIcon color={"disabled"} size={"small"} className={"absolute -right-2 -top-2"}/>
|
|
140
137
|
</div>
|
|
141
138
|
<Paper
|
|
142
|
-
className={
|
|
139
|
+
className={cls(
|
|
143
140
|
"pl-2 w-full flex flex-row gap-4 items-center",
|
|
144
141
|
cardMixin,
|
|
145
142
|
onClick ? cardClickableMixin : "",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import equal from "react-fast-compare"
|
|
3
3
|
|
|
4
4
|
import {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
PropertiesOrBuilders,
|
|
10
10
|
PropertyOrBuilder
|
|
11
11
|
} from "@firecms/core";
|
|
12
|
-
import {
|
|
12
|
+
import { AutorenewIcon, defaultBorderMixin, DragHandleIcon, IconButton, RemoveIcon, Tooltip } from "@firecms/ui";
|
|
13
13
|
import { NonEditablePropertyPreview, PropertyFieldPreview } from "./PropertyFieldPreview";
|
|
14
14
|
import { DragDropContext, Draggable, DraggableProvided, Droppable } from "@hello-pangea/dnd";
|
|
15
15
|
import { getFullId, getFullIdPath } from "./util";
|
|
@@ -48,7 +48,7 @@ export const PropertyTree = React.memo(
|
|
|
48
48
|
|
|
49
49
|
const propertiesOrder = propertiesOrderProp ?? Object.keys(properties);
|
|
50
50
|
|
|
51
|
-
const onDragEnd =
|
|
51
|
+
const onDragEnd = (result: any) => {
|
|
52
52
|
// dropped outside the list
|
|
53
53
|
if (!result.destination) {
|
|
54
54
|
return;
|
|
@@ -61,7 +61,7 @@ export const PropertyTree = React.memo(
|
|
|
61
61
|
newPropertiesOrder.splice(endIndex, 0, removed);
|
|
62
62
|
if (onPropertyMove)
|
|
63
63
|
onPropertyMove(newPropertiesOrder, namespace);
|
|
64
|
-
}
|
|
64
|
+
}
|
|
65
65
|
|
|
66
66
|
return (
|
|
67
67
|
<>
|
|
@@ -224,10 +224,11 @@ export function PropertyTreeEntry({
|
|
|
224
224
|
<div className="absolute top-2 right-2 flex flex-row ">
|
|
225
225
|
|
|
226
226
|
{isPropertyInferred && <Tooltip title={"Inferred property"}>
|
|
227
|
-
<
|
|
227
|
+
<AutorenewIcon size="small" className={"p-2"}/>
|
|
228
228
|
</Tooltip>}
|
|
229
229
|
|
|
230
|
-
{onPropertyRemove && <Tooltip title={"Remove"}
|
|
230
|
+
{onPropertyRemove && <Tooltip title={"Remove"}
|
|
231
|
+
asChild={true}>
|
|
231
232
|
<IconButton size="small"
|
|
232
233
|
color="inherit"
|
|
233
234
|
onClick={() => onPropertyRemove(propertyKey, namespace)}>
|
|
@@ -235,7 +236,8 @@ export function PropertyTreeEntry({
|
|
|
235
236
|
</IconButton>
|
|
236
237
|
</Tooltip>}
|
|
237
238
|
|
|
238
|
-
{onPropertyMove && <Tooltip title={"Move"}
|
|
239
|
+
{onPropertyMove && <Tooltip title={"Move"}
|
|
240
|
+
asChild={true}>
|
|
239
241
|
<IconButton
|
|
240
242
|
component={"span"}
|
|
241
243
|
size="small"
|