@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.
Files changed (73) hide show
  1. package/LICENSE +114 -21
  2. package/dist/ConfigControllerProvider.d.ts +11 -2
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.es.js +10011 -4759
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/index.umd.js +10712 -3
  7. package/dist/index.umd.js.map +1 -1
  8. package/dist/types/collection_editor_controller.d.ts +14 -2
  9. package/dist/types/collection_inference.d.ts +1 -1
  10. package/dist/types/config_permissions.d.ts +2 -2
  11. package/dist/types/persisted_collection.d.ts +1 -1
  12. package/dist/ui/CollectionViewHeaderAction.d.ts +3 -2
  13. package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
  14. package/dist/ui/PropertyAddColumnComponent.d.ts +3 -1
  15. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +4 -3
  16. package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +1 -1
  17. package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -1
  18. package/dist/ui/collection_editor/LayoutModeSwitch.d.ts +5 -0
  19. package/dist/ui/collection_editor/PropertyEditView.d.ts +8 -0
  20. package/dist/ui/collection_editor/PropertyTree.d.ts +9 -9
  21. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +1 -1
  22. package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +7 -0
  23. package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
  24. package/dist/ui/collection_editor/properties/StringPropertyField.d.ts +1 -1
  25. package/dist/useCollectionEditorPlugin.d.ts +17 -11
  26. package/dist/utils/collections.d.ts +6 -0
  27. package/package.json +24 -35
  28. package/src/ConfigControllerProvider.tsx +76 -64
  29. package/src/index.ts +1 -0
  30. package/src/types/collection_editor_controller.tsx +14 -4
  31. package/src/types/collection_inference.ts +1 -1
  32. package/src/types/config_permissions.ts +1 -1
  33. package/src/types/persisted_collection.ts +2 -3
  34. package/src/ui/CollectionViewHeaderAction.tsx +10 -5
  35. package/src/ui/EditorCollectionAction.tsx +10 -63
  36. package/src/ui/EditorCollectionActionStart.tsx +88 -0
  37. package/src/ui/HomePageEditorCollectionAction.tsx +19 -13
  38. package/src/ui/NewCollectionButton.tsx +12 -10
  39. package/src/ui/NewCollectionCard.tsx +3 -3
  40. package/src/ui/PropertyAddColumnComponent.tsx +11 -6
  41. package/src/ui/collection_editor/CollectionDetailsForm.tsx +83 -10
  42. package/src/ui/collection_editor/CollectionEditorDialog.tsx +66 -37
  43. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +8 -7
  44. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +39 -36
  45. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +6 -5
  46. package/src/ui/collection_editor/EnumForm.tsx +10 -6
  47. package/src/ui/collection_editor/GetCodeDialog.tsx +55 -25
  48. package/src/ui/collection_editor/LayoutModeSwitch.tsx +53 -0
  49. package/src/ui/collection_editor/PropertyEditView.tsx +257 -80
  50. package/src/ui/collection_editor/PropertyFieldPreview.tsx +7 -10
  51. package/src/ui/collection_editor/PropertyTree.tsx +9 -7
  52. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +26 -19
  53. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +3 -5
  54. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +26 -9
  55. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +42 -9
  56. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +32 -20
  57. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +54 -47
  58. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -1
  59. package/src/ui/collection_editor/properties/MapPropertyField.tsx +7 -6
  60. package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
  61. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +2 -0
  62. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -1
  63. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +34 -19
  64. package/src/ui/collection_editor/properties/StringPropertyField.tsx +1 -10
  65. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +1 -0
  66. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +2 -2
  67. package/src/ui/collection_editor/templates/pages_template.ts +1 -6
  68. package/src/useCollectionEditorPlugin.tsx +41 -31
  69. package/src/utils/collections.ts +34 -0
  70. package/dist/ui/RootCollectionSuggestions.d.ts +0 -3
  71. package/dist/ui/collection_editor/PropertySelectItem.d.ts +0 -8
  72. package/src/ui/RootCollectionSuggestions.tsx +0 -63
  73. 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
- 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?.();
@@ -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
- if (values?.id && onError) {
386
+ if (values?.id && onError && !isEmptyObject(errors)) {
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,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
- <Select
485
- // className={"w-full"}
486
- error={Boolean(selectedWidgetError)}
487
- value={selectedFieldConfigId ?? ""}
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={setSelectOpen}
491
- position={"item-aligned"}
492
- disabled={disabled}
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
- 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>
509
+ setSelectOpen(open);
525
510
  }}
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>
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
- <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
- }/>}
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
- 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,7 @@ export function PropertyFieldPreview({
56
53
  <PropertyConfigBadge propertyConfig={propertyConfig}/>
57
54
  </div>
58
55
  <Paper
59
- className={cn(
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-gray-500"}>
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
- <RemoveCircleIcon color={"disabled"} size={"small"} className={"absolute -right-2 -top-2"}/>
136
+ <DoNotDisturbOnIcon color={"disabled"} size={"small"} className={"absolute -right-2 -top-2"}/>
140
137
  </div>
141
138
  <Paper
142
- className={cn(
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, { useCallback } from "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 { AutoAwesomeIcon, defaultBorderMixin, DragHandleIcon, IconButton, RemoveIcon, Tooltip } from "@firecms/ui";
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 = useCallback((result: any) => {
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
- }, [namespace, onPropertyMove, propertiesOrder])
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
- <AutoAwesomeIcon size="small" className={"p-2"}/>
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"