@firecms/collection_editor 3.0.0-canary.26 → 3.0.0-canary.261

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 (88) 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 +10742 -4787
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +11412 -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/CollectionEditorImportMapping.d.ts +7 -0
  29. package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
  30. package/dist/ui/collection_editor/properties/ReferencePropertyField.d.ts +2 -1
  31. package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
  32. package/dist/useCollectionEditorPlugin.d.ts +8 -11
  33. package/dist/utils/collections.d.ts +6 -0
  34. package/package.json +23 -35
  35. package/src/ConfigControllerProvider.tsx +64 -66
  36. package/src/index.ts +1 -0
  37. package/src/types/collection_editor_controller.tsx +6 -5
  38. package/src/types/collection_inference.ts +4 -1
  39. package/src/types/config_controller.tsx +4 -1
  40. package/src/types/config_permissions.ts +1 -1
  41. package/src/types/persisted_collection.ts +2 -3
  42. package/src/ui/CollectionViewHeaderAction.tsx +10 -5
  43. package/src/ui/EditorCollectionAction.tsx +12 -70
  44. package/src/ui/EditorCollectionActionStart.tsx +87 -0
  45. package/src/ui/EditorEntityAction.tsx +51 -0
  46. package/src/ui/HomePageEditorCollectionAction.tsx +21 -14
  47. package/src/ui/NewCollectionButton.tsx +1 -1
  48. package/src/ui/NewCollectionCard.tsx +3 -3
  49. package/src/ui/PropertyAddColumnComponent.tsx +11 -6
  50. package/src/ui/collection_editor/CollectionDetailsForm.tsx +157 -50
  51. package/src/ui/collection_editor/CollectionEditorDialog.tsx +117 -37
  52. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +23 -32
  53. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +46 -41
  54. package/src/ui/collection_editor/EntityActionsEditTab.tsx +163 -0
  55. package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +41 -0
  56. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +11 -7
  57. package/src/ui/collection_editor/EnumForm.tsx +11 -7
  58. package/src/ui/collection_editor/GetCodeDialog.tsx +60 -28
  59. package/src/ui/collection_editor/LayoutModeSwitch.tsx +54 -0
  60. package/src/ui/collection_editor/PropertyEditView.tsx +264 -78
  61. package/src/ui/collection_editor/PropertyFieldPreview.tsx +8 -10
  62. package/src/ui/collection_editor/PropertyTree.tsx +184 -138
  63. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +26 -19
  64. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +9 -7
  65. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +33 -9
  66. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +42 -9
  67. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +32 -20
  68. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +54 -47
  69. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -1
  70. package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -7
  71. package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
  72. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +7 -3
  73. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
  74. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +34 -19
  75. package/src/ui/collection_editor/properties/StringPropertyField.tsx +1 -10
  76. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +1 -0
  77. package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +2 -0
  78. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +2 -2
  79. package/src/ui/collection_editor/templates/pages_template.ts +1 -6
  80. package/src/ui/collection_editor/utils/strings.ts +13 -6
  81. package/src/ui/collection_editor/utils/supported_fields.tsx +1 -0
  82. package/src/ui/collection_editor/utils/update_property_for_widget.ts +9 -0
  83. package/src/useCollectionEditorPlugin.tsx +38 -32
  84. package/src/utils/collections.ts +46 -0
  85. package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
  86. package/dist/ui/collection_editor/PropertySelectItem.d.ts +0 -8
  87. package/src/ui/RootCollectionSuggestions.tsx +0 -63
  88. 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,
@@ -148,7 +157,10 @@ export const PropertyForm = React.memo(
148
157
  } = newPropertyWithId;
149
158
  doOnPropertyChanged({
150
159
  id,
151
- property: { ...property, editable: property.editable ?? true }
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?.();
@@ -278,6 +295,7 @@ export function PropertyFormDialog({
278
295
 
279
296
  {onCancel && <Button
280
297
  variant={"text"}
298
+ color={"primary"}
281
299
  onClick={() => {
282
300
  onCancel();
283
301
  formexRef.current?.resetForm();
@@ -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, onPropertyChanged, propertyNamespace]);
384
+ }, [deferredValues, includeIdAndTitle, propertyNamespace]);
371
385
 
372
386
  useEffect(() => {
373
387
  if (values?.id && onError) {
374
388
  onError(values?.id, propertyNamespace, errors);
375
389
  }
376
- }, [errors, onError, propertyNamespace, values?.id]);
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
@@ -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,205 @@ 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
+ number_input: "Number",
611
+ number_select: "Select",
612
+ multi_number_select: "Select",
613
+ file_upload: "File",
614
+ multi_file_upload: "File",
615
+ reference: "Reference",
616
+ reference_as_string: "Text",
617
+ multi_references: "Reference",
618
+ date_time: "Date",
619
+ group: "Group",
620
+ key_value: "Group",
621
+ repeat: "Array",
622
+ custom_array: "Array",
623
+ block: "Group"
624
+ };
625
+
626
+ function WidgetSelectView({
627
+ initialProperty,
628
+ value,
629
+ onValueChange,
630
+ open,
631
+ onOpenChange,
632
+ disabled,
633
+ showError,
634
+ existing,
635
+ propertyConfigs,
636
+ inArray
637
+ }: {
638
+ initialProperty?: PropertyWithId,
639
+ value?: PropertyConfigId,
640
+ onValueChange: (value: string) => void,
641
+ showError: boolean,
642
+ open: boolean,
643
+ onOpenChange: (open: boolean, hasValue: boolean) => void,
644
+ disabled: boolean,
645
+ existing: boolean,
646
+ propertyConfigs: Record<string, PropertyConfig>,
647
+ inArray?: boolean
648
+ }) {
649
+
650
+ const allSupportedFields = Object.entries(supportedFields).concat(Object.entries(propertyConfigs));
651
+
652
+ const displayedWidgets = (inArray
653
+ ? allSupportedFields.filter(([_, propertyConfig]) => !isPropertyBuilder(propertyConfig.property) && propertyConfig.property?.dataType !== "array")
654
+ : allSupportedFields)
655
+ .map(([key, propertyConfig]) => ({
656
+ [key]: propertyConfig
657
+ }))
658
+ .reduce((a, b) => {
659
+ return {
660
+ ...a,
661
+ ...b
662
+ }
663
+ }, {});
664
+
665
+ const key = value;
666
+ const propertyConfig = key ? (DEFAULT_FIELD_CONFIGS[key] ?? propertyConfigs[key]) : undefined;
667
+ const baseProperty = propertyConfig?.property;
668
+ const baseFieldConfig = baseProperty && !isPropertyBuilder(baseProperty) ? getFieldConfig(baseProperty, propertyConfigs) : undefined;
669
+ const computedFieldConfig = baseFieldConfig && propertyConfig ? mergeDeep(baseFieldConfig, propertyConfig) : propertyConfig;
670
+
671
+ const groups: string[] = [...new Set(Object.keys(displayedWidgets).map(key => {
672
+ const group = WIDGET_TYPE_MAP[key as PropertyConfigId];
673
+ if (group) {
674
+ return group;
675
+ }
676
+ return "Custom/Other"
677
+ }))];
678
+
679
+ return <>
680
+ <div
681
+ onClick={() => {
682
+ if (!disabled) {
683
+ onOpenChange(!open, Boolean(value));
684
+ }
685
+ }}
686
+ className={cls(
687
+ "select-none rounded-md text-sm p-4",
688
+ fieldBackgroundMixin,
689
+ disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
690
+ "relative flex items-center",
691
+ )}>
692
+ {!value && <em>Select a property widget</em>}
693
+ {value && computedFieldConfig && <div
694
+ className={cls(
695
+ "flex items-center")}>
696
+ <div className={"mr-8"}>
697
+ <PropertyConfigBadge propertyConfig={computedFieldConfig}/>
698
+ </div>
699
+ <div className={"flex flex-col items-start text-base text-left"}>
700
+ <div>{computedFieldConfig.name}</div>
701
+ <Typography variant={"caption"}
702
+ color={"secondary"}>
703
+ {computedFieldConfig.description}
704
+ </Typography>
705
+ </div>
706
+ </div>}
707
+ </div>
708
+ <Dialog open={open}
709
+ onOpenChange={(open) => onOpenChange(open, Boolean(value))}
710
+ maxWidth={"4xl"}>
711
+ <DialogTitle>
712
+ Select a property widget
713
+ </DialogTitle>
714
+ <DialogContent>
715
+ <div>
716
+ {groups.map(group => {
717
+ return <div key={group} className={"mt-4"}>
718
+ <Typography variant={"label"}>{group}</Typography>
719
+ <div className={"grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2 mt-4"}>
720
+ {Object.entries(displayedWidgets).map(([key, propertyConfig]) => {
721
+ const groupKey = WIDGET_TYPE_MAP[key as PropertyConfigId];
722
+ if (groupKey === group) {
723
+ return <WidgetSelectViewItem
724
+ key={key}
725
+ initialProperty={initialProperty}
726
+ onClick={() => {
727
+ onValueChange(key);
728
+ onOpenChange(false, true);
729
+ }}
730
+ propertyConfig={propertyConfig}
731
+ existing={existing}/>;
732
+ }
733
+ return null;
734
+ })}
735
+ </div>
736
+ </div>;
737
+ })}
738
+ {/*{displayedWidgets.map(([key, propertyConfig]) => {*/}
739
+ {/* return <WidgetSelectViewItem*/}
740
+ {/* key={key}*/}
741
+ {/* initialProperty={initialProperty}*/}
742
+ {/* onClick={() => {*/}
743
+ {/* onValueChange(key);*/}
744
+ {/* onOpenChange(false);*/}
745
+ {/* }}*/}
746
+ {/* propertyConfig={propertyConfig}*/}
747
+ {/* existing={existing}/>;*/}
748
+ {/*})}*/}
749
+ </div>
750
+ </DialogContent>
751
+ </Dialog>
752
+ </>;
753
+ }
754
+
755
+ export interface PropertySelectItemProps {
756
+ onClick?: () => void;
757
+ initialProperty?: PropertyWithId;
758
+ propertyConfig: PropertyConfig;
759
+ existing: boolean;
760
+ }
761
+
762
+ export function WidgetSelectViewItem({
763
+ onClick,
764
+ initialProperty,
765
+ // optionDisabled,
766
+ propertyConfig,
767
+ existing
768
+ }: PropertySelectItemProps) {
769
+ const baseProperty = propertyConfig.property;
770
+ const shouldWarnChangingDataType = existing && !isPropertyBuilder(baseProperty) && baseProperty.dataType !== initialProperty?.dataType;
771
+
772
+ return <Card
773
+ onClick={onClick}
774
+ className={"flex flex-row items-center px-4 py-2 m-1"}>
775
+ <div
776
+ className={cls(
777
+ "flex flex-row items-center text-base min-h-[48px]",
778
+ )}>
779
+ <div className={"mr-8"}>
780
+ <PropertyConfigBadge propertyConfig={propertyConfig} disabled={shouldWarnChangingDataType}/>
781
+ </div>
782
+ <div>
783
+ <div className={"flex flex-row gap-2 items-center"}>
784
+ {shouldWarnChangingDataType && <Tooltip
785
+ title={"This widget uses a different data type than the initially selected widget. This can cause errors with existing data."}>
786
+ <WarningIcon size="smallest" className={"w-4"}/>
787
+ </Tooltip>}
788
+ <Typography
789
+ color={shouldWarnChangingDataType ? "secondary" : undefined}>{propertyConfig.name}</Typography>
790
+ </div>
791
+
792
+ <Typography variant={"caption"}
793
+ color={"secondary"}
794
+ className={"max-w-sm"}>
795
+ {propertyConfig.description}
796
+ </Typography>
797
+
798
+ </div>
799
+ </div>
800
+ </Card>
801
+ }
@@ -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 : "",