@firecms/collection_editor 3.0.0-canary.17 → 3.0.0-canary.170
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 +11 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +10011 -4759
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +10712 -3
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +14 -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/CollectionEditorDialog.d.ts +4 -3
- 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 +17 -11
- package/dist/utils/collections.d.ts +6 -0
- package/package.json +24 -35
- package/src/ConfigControllerProvider.tsx +76 -64
- package/src/index.ts +1 -0
- package/src/types/collection_editor_controller.tsx +14 -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 +12 -10
- package/src/ui/NewCollectionCard.tsx +3 -3
- package/src/ui/PropertyAddColumnComponent.tsx +11 -6
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +83 -10
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +66 -37
- 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 +55 -25
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +53 -0
- package/src/ui/collection_editor/PropertyEditView.tsx +257 -80
- 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 +41 -31
- package/src/utils/collections.ts +34 -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,35 @@ 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
|
-
getFieldId,
|
|
9
|
+
getFieldId, isEmptyObject,
|
|
11
10
|
isPropertyBuilder,
|
|
12
11
|
isValidRegExp,
|
|
13
12
|
mergeDeep,
|
|
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,
|
|
@@ -148,7 +157,10 @@ export const PropertyForm = React.memo(
|
|
|
148
157
|
} = newPropertyWithId;
|
|
149
158
|
doOnPropertyChanged({
|
|
150
159
|
id,
|
|
151
|
-
property: {
|
|
160
|
+
property: {
|
|
161
|
+
...property,
|
|
162
|
+
editable: property.editable ?? true
|
|
163
|
+
}
|
|
152
164
|
});
|
|
153
165
|
if (!existingProperty)
|
|
154
166
|
controller.resetForm({ values: initialValue });
|
|
@@ -209,6 +221,7 @@ export const PropertyForm = React.memo(
|
|
|
209
221
|
includeIdAndTitle={includeIdAndName}
|
|
210
222
|
propertyNamespace={propertyNamespace}
|
|
211
223
|
onError={onError}
|
|
224
|
+
onDismiss={onDismiss}
|
|
212
225
|
showErrors={forceShowErrors || formexController.submitCount > 0}
|
|
213
226
|
existing={existingProperty}
|
|
214
227
|
autoUpdateId={autoUpdateId}
|
|
@@ -228,6 +241,7 @@ export const PropertyForm = React.memo(
|
|
|
228
241
|
a.includeIdAndName === b.includeIdAndName &&
|
|
229
242
|
a.autoOpenTypeSelect === b.autoOpenTypeSelect &&
|
|
230
243
|
a.autoUpdateId === b.autoUpdateId &&
|
|
244
|
+
a.existingPropertyKeys === b.existingPropertyKeys &&
|
|
231
245
|
a.existingProperty === b.existingProperty
|
|
232
246
|
);
|
|
233
247
|
|
|
@@ -261,8 +275,11 @@ export function PropertyFormDialog({
|
|
|
261
275
|
e.stopPropagation();
|
|
262
276
|
formexRef.current?.handleSubmit(e)
|
|
263
277
|
}}>
|
|
278
|
+
<DialogTitle hidden>Property edit view</DialogTitle>
|
|
264
279
|
<DialogContent>
|
|
280
|
+
|
|
265
281
|
<PropertyForm {...formProps}
|
|
282
|
+
onDismiss={onCancel}
|
|
266
283
|
onPropertyChanged={(params) => {
|
|
267
284
|
onPropertyChanged?.(params);
|
|
268
285
|
onOkClicked?.();
|
|
@@ -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
|
-
if (values?.id && onError) {
|
|
386
|
+
if (values?.id && onError && !isEmptyObject(errors)) {
|
|
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,6 @@ function PropertyEditFormFields({
|
|
|
387
400
|
let childComponent;
|
|
388
401
|
if (selectedFieldConfigId === "text_field" ||
|
|
389
402
|
selectedFieldConfigId === "multiline" ||
|
|
390
|
-
selectedFieldConfigId === "markdown" ||
|
|
391
403
|
selectedFieldConfigId === "email") {
|
|
392
404
|
childComponent =
|
|
393
405
|
<StringPropertyField widgetId={selectedFieldConfigId}
|
|
@@ -397,6 +409,10 @@ function PropertyEditFormFields({
|
|
|
397
409
|
childComponent =
|
|
398
410
|
<UrlPropertyField disabled={disabled}
|
|
399
411
|
showErrors={showErrors}/>;
|
|
412
|
+
} else if (selectedFieldConfigId === "markdown") {
|
|
413
|
+
childComponent =
|
|
414
|
+
<MarkdownPropertyField disabled={disabled}
|
|
415
|
+
showErrors={showErrors}/>;
|
|
400
416
|
} else if (selectedFieldConfigId === "select" ||
|
|
401
417
|
selectedFieldConfigId === "number_select") {
|
|
402
418
|
childComponent = <EnumPropertyField
|
|
@@ -481,62 +497,22 @@ function PropertyEditFormFields({
|
|
|
481
497
|
|
|
482
498
|
<div className="flex mt-2 justify-between">
|
|
483
499
|
<div className={"w-full flex flex-col gap-2"}>
|
|
484
|
-
<
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
placeholder={"Select a property widget"}
|
|
500
|
+
<WidgetSelectView
|
|
501
|
+
initialProperty={values}
|
|
502
|
+
value={selectedFieldConfigId as PropertyConfigId}
|
|
503
|
+
onValueChange={(value) => onWidgetSelectChanged(value as PropertyConfigId)}
|
|
489
504
|
open={selectOpen}
|
|
490
|
-
onOpenChange={
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
renderValue={(value) => {
|
|
494
|
-
if (!value) {
|
|
495
|
-
return <em>Select a property
|
|
496
|
-
widget</em>;
|
|
505
|
+
onOpenChange={(open, hasValue) => {
|
|
506
|
+
if (!hasValue) {
|
|
507
|
+
onDismiss?.();
|
|
497
508
|
}
|
|
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>
|
|
509
|
+
setSelectOpen(open);
|
|
525
510
|
}}
|
|
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>
|
|
511
|
+
disabled={disabled}
|
|
512
|
+
showError={Boolean(selectedWidgetError)}
|
|
513
|
+
existing={existing}
|
|
514
|
+
propertyConfigs={propertyConfigs}
|
|
515
|
+
inArray={inArray}/>
|
|
540
516
|
|
|
541
517
|
{selectedWidgetError &&
|
|
542
518
|
<Typography variant="caption"
|
|
@@ -575,15 +551,15 @@ function PropertyEditFormFields({
|
|
|
575
551
|
</div>
|
|
576
552
|
|
|
577
553
|
{onDelete &&
|
|
578
|
-
<
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
554
|
+
<ConfirmationDialog open={deleteDialogOpen}
|
|
555
|
+
onAccept={() => onDelete(values?.id, propertyNamespace)}
|
|
556
|
+
onCancel={() => setDeleteDialogOpen(false)}
|
|
557
|
+
title={<div>Delete this property?</div>}
|
|
558
|
+
body={
|
|
559
|
+
<div> This will <b>not delete any
|
|
560
|
+
data</b>, only modify the
|
|
561
|
+
collection.</div>
|
|
562
|
+
}/>}
|
|
587
563
|
|
|
588
564
|
</>
|
|
589
565
|
);
|
|
@@ -613,3 +589,204 @@ function validateName(value: string) {
|
|
|
613
589
|
}
|
|
614
590
|
return error;
|
|
615
591
|
}
|
|
592
|
+
|
|
593
|
+
const WIDGET_TYPE_MAP: Record<PropertyConfigId, string> = {
|
|
594
|
+
text_field: "Text",
|
|
595
|
+
multiline: "Text",
|
|
596
|
+
markdown: "Text",
|
|
597
|
+
url: "Text",
|
|
598
|
+
email: "Text",
|
|
599
|
+
switch: "Boolean",
|
|
600
|
+
select: "Select",
|
|
601
|
+
multi_select: "Select",
|
|
602
|
+
number_input: "Number",
|
|
603
|
+
number_select: "Select",
|
|
604
|
+
multi_number_select: "Select",
|
|
605
|
+
file_upload: "File",
|
|
606
|
+
multi_file_upload: "File",
|
|
607
|
+
reference: "Reference",
|
|
608
|
+
multi_references: "Reference",
|
|
609
|
+
date_time: "Date",
|
|
610
|
+
group: "Group",
|
|
611
|
+
key_value: "Group",
|
|
612
|
+
repeat: "Array",
|
|
613
|
+
custom_array: "Array",
|
|
614
|
+
block: "Group"
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
function WidgetSelectView({
|
|
618
|
+
initialProperty,
|
|
619
|
+
value,
|
|
620
|
+
onValueChange,
|
|
621
|
+
open,
|
|
622
|
+
onOpenChange,
|
|
623
|
+
disabled,
|
|
624
|
+
showError,
|
|
625
|
+
existing,
|
|
626
|
+
propertyConfigs,
|
|
627
|
+
inArray
|
|
628
|
+
}: {
|
|
629
|
+
initialProperty?: PropertyWithId,
|
|
630
|
+
value?: PropertyConfigId,
|
|
631
|
+
onValueChange: (value: string) => void,
|
|
632
|
+
showError: boolean,
|
|
633
|
+
open: boolean,
|
|
634
|
+
onOpenChange: (open: boolean, hasValue: boolean) => void,
|
|
635
|
+
disabled: boolean,
|
|
636
|
+
existing: boolean,
|
|
637
|
+
propertyConfigs: Record<string, PropertyConfig>,
|
|
638
|
+
inArray?: boolean
|
|
639
|
+
}) {
|
|
640
|
+
|
|
641
|
+
const allSupportedFields = Object.entries(supportedFields).concat(Object.entries(propertyConfigs));
|
|
642
|
+
|
|
643
|
+
const displayedWidgets = (inArray
|
|
644
|
+
? allSupportedFields.filter(([_, propertyConfig]) => !isPropertyBuilder(propertyConfig.property) && propertyConfig.property?.dataType !== "array")
|
|
645
|
+
: allSupportedFields)
|
|
646
|
+
.map(([key, propertyConfig]) => ({
|
|
647
|
+
[key]: propertyConfig
|
|
648
|
+
}))
|
|
649
|
+
.reduce((a, b) => {
|
|
650
|
+
return {
|
|
651
|
+
...a,
|
|
652
|
+
...b
|
|
653
|
+
}
|
|
654
|
+
}, {});
|
|
655
|
+
|
|
656
|
+
const key = value;
|
|
657
|
+
const propertyConfig = key ? (DEFAULT_FIELD_CONFIGS[key] ?? propertyConfigs[key]) : undefined;
|
|
658
|
+
const baseProperty = propertyConfig?.property;
|
|
659
|
+
const baseFieldConfig = baseProperty && !isPropertyBuilder(baseProperty) ? getFieldConfig(baseProperty, propertyConfigs) : undefined;
|
|
660
|
+
const computedFieldConfig = baseFieldConfig && propertyConfig ? mergeDeep(baseFieldConfig, propertyConfig) : propertyConfig;
|
|
661
|
+
|
|
662
|
+
const groups: string[] = [...new Set(Object.keys(displayedWidgets).map(key => {
|
|
663
|
+
const group = WIDGET_TYPE_MAP[key as PropertyConfigId];
|
|
664
|
+
if (group) {
|
|
665
|
+
return group;
|
|
666
|
+
}
|
|
667
|
+
return "Custom/Other"
|
|
668
|
+
}))];
|
|
669
|
+
|
|
670
|
+
return <>
|
|
671
|
+
<div
|
|
672
|
+
onClick={() => {
|
|
673
|
+
if (!disabled) {
|
|
674
|
+
onOpenChange(!open, Boolean(value));
|
|
675
|
+
}
|
|
676
|
+
}}
|
|
677
|
+
className={cls(
|
|
678
|
+
"select-none rounded-md text-sm p-4",
|
|
679
|
+
fieldBackgroundMixin,
|
|
680
|
+
disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
|
|
681
|
+
"relative flex items-center",
|
|
682
|
+
)}>
|
|
683
|
+
{!value && <em>Select a property widget</em>}
|
|
684
|
+
{value && computedFieldConfig && <div
|
|
685
|
+
className={cls(
|
|
686
|
+
"flex items-center")}>
|
|
687
|
+
<div className={"mr-8"}>
|
|
688
|
+
<PropertyConfigBadge propertyConfig={computedFieldConfig}/>
|
|
689
|
+
</div>
|
|
690
|
+
<div className={"flex flex-col items-start text-base text-left"}>
|
|
691
|
+
<div>{computedFieldConfig.name}</div>
|
|
692
|
+
<Typography variant={"caption"}
|
|
693
|
+
color={"secondary"}>
|
|
694
|
+
{computedFieldConfig.description}
|
|
695
|
+
</Typography>
|
|
696
|
+
</div>
|
|
697
|
+
</div>}
|
|
698
|
+
</div>
|
|
699
|
+
<Dialog open={open}
|
|
700
|
+
onOpenChange={(open) => onOpenChange(open, Boolean(value))}
|
|
701
|
+
maxWidth={"4xl"}>
|
|
702
|
+
<DialogTitle>
|
|
703
|
+
Select a property widget
|
|
704
|
+
</DialogTitle>
|
|
705
|
+
<DialogContent>
|
|
706
|
+
<div>
|
|
707
|
+
{groups.map(group => {
|
|
708
|
+
return <div key={group} className={"mt-4"}>
|
|
709
|
+
<Typography variant={"label"}>{group}</Typography>
|
|
710
|
+
<div className={"grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2 mt-4"}>
|
|
711
|
+
{Object.entries(displayedWidgets).map(([key, propertyConfig]) => {
|
|
712
|
+
const groupKey = WIDGET_TYPE_MAP[key as PropertyConfigId];
|
|
713
|
+
if (groupKey === group) {
|
|
714
|
+
return <WidgetSelectViewItem
|
|
715
|
+
key={key}
|
|
716
|
+
initialProperty={initialProperty}
|
|
717
|
+
onClick={() => {
|
|
718
|
+
onValueChange(key);
|
|
719
|
+
onOpenChange(false, true);
|
|
720
|
+
}}
|
|
721
|
+
propertyConfig={propertyConfig}
|
|
722
|
+
existing={existing}/>;
|
|
723
|
+
}
|
|
724
|
+
return null;
|
|
725
|
+
})}
|
|
726
|
+
</div>
|
|
727
|
+
</div>;
|
|
728
|
+
})}
|
|
729
|
+
{/*{displayedWidgets.map(([key, propertyConfig]) => {*/}
|
|
730
|
+
{/* return <WidgetSelectViewItem*/}
|
|
731
|
+
{/* key={key}*/}
|
|
732
|
+
{/* initialProperty={initialProperty}*/}
|
|
733
|
+
{/* onClick={() => {*/}
|
|
734
|
+
{/* onValueChange(key);*/}
|
|
735
|
+
{/* onOpenChange(false);*/}
|
|
736
|
+
{/* }}*/}
|
|
737
|
+
{/* propertyConfig={propertyConfig}*/}
|
|
738
|
+
{/* existing={existing}/>;*/}
|
|
739
|
+
{/*})}*/}
|
|
740
|
+
</div>
|
|
741
|
+
</DialogContent>
|
|
742
|
+
</Dialog>
|
|
743
|
+
</>;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
export interface PropertySelectItemProps {
|
|
747
|
+
onClick?: () => void;
|
|
748
|
+
initialProperty?: PropertyWithId;
|
|
749
|
+
propertyConfig: PropertyConfig;
|
|
750
|
+
existing: boolean;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
export function WidgetSelectViewItem({
|
|
754
|
+
onClick,
|
|
755
|
+
initialProperty,
|
|
756
|
+
// optionDisabled,
|
|
757
|
+
propertyConfig,
|
|
758
|
+
existing
|
|
759
|
+
}: PropertySelectItemProps) {
|
|
760
|
+
const baseProperty = propertyConfig.property;
|
|
761
|
+
const shouldWarnChangingDataType = existing && !isPropertyBuilder(baseProperty) && baseProperty.dataType !== initialProperty?.dataType;
|
|
762
|
+
|
|
763
|
+
return <Card
|
|
764
|
+
onClick={onClick}
|
|
765
|
+
className={"flex flex-row items-center px-4 py-2"}>
|
|
766
|
+
<div
|
|
767
|
+
className={cls(
|
|
768
|
+
"flex flex-row items-center text-base min-h-[48px]",
|
|
769
|
+
)}>
|
|
770
|
+
<div className={"mr-8"}>
|
|
771
|
+
<PropertyConfigBadge propertyConfig={propertyConfig} disabled={shouldWarnChangingDataType}/>
|
|
772
|
+
</div>
|
|
773
|
+
<div>
|
|
774
|
+
<div className={"flex flex-row gap-2 items-center"}>
|
|
775
|
+
{shouldWarnChangingDataType && <Tooltip
|
|
776
|
+
title={"This widget uses a different data type than the initially selected widget. This can cause errors with existing data."}>
|
|
777
|
+
<WarningIcon size="smallest" className={"w-4"}/>
|
|
778
|
+
</Tooltip>}
|
|
779
|
+
<Typography
|
|
780
|
+
color={shouldWarnChangingDataType ? "secondary" : undefined}>{propertyConfig.name}</Typography>
|
|
781
|
+
</div>
|
|
782
|
+
|
|
783
|
+
<Typography variant={"caption"}
|
|
784
|
+
color={"secondary"}
|
|
785
|
+
className={"max-w-sm"}>
|
|
786
|
+
{propertyConfig.description}
|
|
787
|
+
</Typography>
|
|
788
|
+
|
|
789
|
+
</div>
|
|
790
|
+
</div>
|
|
791
|
+
</Card>
|
|
792
|
+
}
|
|
@@ -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"
|