@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.
Files changed (91) hide show
  1. package/LICENSE +114 -21
  2. package/README.md +165 -1
  3. package/dist/ConfigControllerProvider.d.ts +1 -2
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.es.js +10792 -4791
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +11458 -3
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/types/collection_editor_controller.d.ts +3 -2
  10. package/dist/types/collection_inference.d.ts +4 -1
  11. package/dist/types/config_controller.d.ts +3 -1
  12. package/dist/types/config_permissions.d.ts +2 -2
  13. package/dist/types/persisted_collection.d.ts +1 -1
  14. package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
  15. package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
  16. package/dist/ui/EditorEntityAction.d.ts +2 -0
  17. package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
  18. package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +3 -1
  19. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +3 -2
  20. package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +2 -2
  21. package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +2 -2
  22. package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +4 -0
  23. package/dist/ui/collection_editor/EntityActionsSelectDialog.d.ts +4 -0
  24. package/dist/ui/collection_editor/LayoutModeSwitch.d.ts +5 -0
  25. package/dist/ui/collection_editor/PropertyEditView.d.ts +8 -0
  26. package/dist/ui/collection_editor/PropertyTree.d.ts +11 -12
  27. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
  28. package/dist/ui/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
  29. package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +8 -1
  30. package/dist/ui/collection_editor/import/clean_import_data.d.ts +1 -1
  31. package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
  32. package/dist/ui/collection_editor/properties/ReferencePropertyField.d.ts +2 -1
  33. package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
  34. package/dist/useCollectionEditorPlugin.d.ts +8 -11
  35. package/dist/utils/collections.d.ts +6 -0
  36. package/package.json +25 -37
  37. package/src/ConfigControllerProvider.tsx +64 -66
  38. package/src/index.ts +1 -0
  39. package/src/types/collection_editor_controller.tsx +6 -5
  40. package/src/types/collection_inference.ts +4 -1
  41. package/src/types/config_controller.tsx +4 -1
  42. package/src/types/config_permissions.ts +1 -1
  43. package/src/types/persisted_collection.ts +2 -3
  44. package/src/ui/CollectionViewHeaderAction.tsx +10 -5
  45. package/src/ui/EditorCollectionAction.tsx +12 -70
  46. package/src/ui/EditorCollectionActionStart.tsx +87 -0
  47. package/src/ui/EditorEntityAction.tsx +51 -0
  48. package/src/ui/HomePageEditorCollectionAction.tsx +21 -14
  49. package/src/ui/NewCollectionButton.tsx +1 -1
  50. package/src/ui/NewCollectionCard.tsx +3 -3
  51. package/src/ui/PropertyAddColumnComponent.tsx +11 -6
  52. package/src/ui/collection_editor/CollectionDetailsForm.tsx +157 -50
  53. package/src/ui/collection_editor/CollectionEditorDialog.tsx +119 -39
  54. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +24 -33
  55. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +46 -41
  56. package/src/ui/collection_editor/EntityActionsEditTab.tsx +163 -0
  57. package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +41 -0
  58. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +11 -7
  59. package/src/ui/collection_editor/EnumForm.tsx +11 -7
  60. package/src/ui/collection_editor/GetCodeDialog.tsx +60 -28
  61. package/src/ui/collection_editor/LayoutModeSwitch.tsx +54 -0
  62. package/src/ui/collection_editor/PropertyEditView.tsx +266 -79
  63. package/src/ui/collection_editor/PropertyFieldPreview.tsx +8 -10
  64. package/src/ui/collection_editor/PropertyTree.tsx +184 -138
  65. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +26 -19
  66. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +9 -7
  67. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +41 -9
  68. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +43 -10
  69. package/src/ui/collection_editor/import/clean_import_data.ts +1 -1
  70. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +32 -20
  71. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +54 -47
  72. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -1
  73. package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -7
  74. package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
  75. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +7 -3
  76. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
  77. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +34 -19
  78. package/src/ui/collection_editor/properties/StringPropertyField.tsx +4 -9
  79. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +1 -0
  80. package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +2 -0
  81. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +2 -2
  82. package/src/ui/collection_editor/templates/pages_template.ts +1 -6
  83. package/src/ui/collection_editor/utils/strings.ts +13 -6
  84. package/src/ui/collection_editor/utils/supported_fields.tsx +2 -0
  85. package/src/ui/collection_editor/utils/update_property_for_widget.ts +37 -6
  86. package/src/useCollectionEditorPlugin.tsx +38 -32
  87. package/src/utils/collections.ts +46 -0
  88. package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
  89. package/dist/ui/collection_editor/PropertySelectItem.d.ts +0 -8
  90. package/src/ui/RootCollectionSuggestions.tsx +0 -63
  91. 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
- cn,
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
- Select,
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: { ...property, editable: property.editable ?? true }
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, onPropertyChanged, propertyNamespace]);
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, onError, propertyNamespace, values?.id]);
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 === "markdown" ||
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
- <Select
485
- // className={"w-full"}
486
- error={Boolean(selectedWidgetError)}
487
- value={selectedFieldConfigId ?? ""}
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={setSelectOpen}
491
- position={"item-aligned"}
492
- disabled={disabled}
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
- const key = value as PropertyConfigId;
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
- onValueChange={(value) => {
527
- onWidgetSelectChanged(value as PropertyConfigId);
528
- }}>
529
- {displayedWidgets.map(([key, propertyConfig]) => {
530
- const baseProperty = propertyConfig.property;
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
- <DeleteConfirmationDialog open={deleteDialogOpen}
579
- onAccept={() => onDelete(values?.id, propertyNamespace)}
580
- onCancel={() => setDeleteDialogOpen(false)}
581
- title={<div>Delete this property?</div>}
582
- body={
583
- <div> This will <b>not delete any
584
- data</b>, only modify the
585
- collection.</div>
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
- cn,
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={cn(
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-gray-500"}>
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
- <RemoveCircleIcon color={"disabled"} size={"small"} className={"absolute -right-2 -top-2"}/>
137
+ <DoNotDisturbOnIcon color={"disabled"} size={"small"} className={"absolute -right-2 -top-2"}/>
140
138
  </div>
141
139
  <Paper
142
- className={cn(
140
+ className={cls(
143
141
  "pl-2 w-full flex flex-row gap-4 items-center",
144
142
  cardMixin,
145
143
  onClick ? cardClickableMixin : "",