@firecms/core 3.0.0-beta.13 → 3.0.0-beta.15

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 (124) hide show
  1. package/dist/components/ArrayContainer.d.ts +7 -12
  2. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
  3. package/dist/components/EntityPreview.d.ts +4 -2
  4. package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
  5. package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
  6. package/dist/components/HomePage/NavigationCard.d.ts +3 -1
  7. package/dist/components/HomePage/NavigationCardBinding.d.ts +3 -2
  8. package/dist/components/HomePage/NavigationGroup.d.ts +7 -1
  9. package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
  10. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  11. package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
  12. package/dist/components/index.d.ts +1 -0
  13. package/dist/core/EntityEditView.d.ts +3 -0
  14. package/dist/core/EntityEditViewFormActions.d.ts +1 -1
  15. package/dist/core/field_configs.d.ts +1 -1
  16. package/dist/form/EntityForm.d.ts +2 -1
  17. package/dist/form/EntityFormActions.d.ts +6 -2
  18. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  19. package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
  20. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
  21. package/dist/form/index.d.ts +1 -0
  22. package/dist/hooks/useBuildNavigationController.d.ts +50 -8
  23. package/dist/index.es.js +3259 -1649
  24. package/dist/index.es.js.map +1 -1
  25. package/dist/index.umd.js +3265 -1658
  26. package/dist/index.umd.js.map +1 -1
  27. package/dist/types/analytics.d.ts +1 -1
  28. package/dist/types/collections.d.ts +28 -4
  29. package/dist/types/customization_controller.d.ts +8 -0
  30. package/dist/types/entity_actions.d.ts +46 -6
  31. package/dist/types/firecms.d.ts +10 -1
  32. package/dist/types/navigation.d.ts +26 -5
  33. package/dist/types/plugins.d.ts +32 -1
  34. package/dist/types/properties.d.ts +7 -0
  35. package/dist/types/property_config.d.ts +1 -1
  36. package/dist/types/side_entity_controller.d.ts +8 -0
  37. package/dist/util/callbacks.d.ts +2 -0
  38. package/dist/util/icons.d.ts +2 -2
  39. package/dist/util/index.d.ts +1 -0
  40. package/dist/util/navigation_from_path.d.ts +4 -0
  41. package/dist/util/resolutions.d.ts +2 -1
  42. package/package.json +8 -6
  43. package/src/components/ArrayContainer.tsx +409 -294
  44. package/src/components/ClearFilterSortButton.tsx +1 -1
  45. package/src/components/ConfirmationDialog.tsx +10 -9
  46. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +6 -0
  47. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +26 -4
  48. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
  49. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
  50. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +53 -27
  51. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
  52. package/src/components/EntityCollectionView/EntityCollectionView.tsx +11 -4
  53. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +3 -2
  54. package/src/components/EntityPreview.tsx +18 -14
  55. package/src/components/ErrorView.tsx +1 -1
  56. package/src/components/FireCMSLogo.tsx +7 -51
  57. package/src/components/HomePage/DefaultHomePage.tsx +491 -156
  58. package/src/components/HomePage/FavouritesView.tsx +3 -3
  59. package/src/components/HomePage/HomePageDnD.tsx +599 -0
  60. package/src/components/HomePage/NavigationCard.tsx +47 -38
  61. package/src/components/HomePage/NavigationCardBinding.tsx +13 -7
  62. package/src/components/HomePage/NavigationGroup.tsx +63 -29
  63. package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
  64. package/src/components/SelectableTable/SelectableTable.tsx +140 -143
  65. package/src/components/UnsavedChangesDialog.tsx +6 -2
  66. package/src/components/VirtualTable/VirtualTable.tsx +8 -30
  67. package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
  68. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
  69. package/src/components/common/default_entity_actions.tsx +25 -9
  70. package/src/components/common/useDataSourceTableController.tsx +2 -2
  71. package/src/components/index.tsx +2 -0
  72. package/src/core/DefaultDrawer.tsx +8 -8
  73. package/src/core/DrawerNavigationItem.tsx +1 -1
  74. package/src/core/EntityEditView.tsx +65 -18
  75. package/src/core/EntityEditViewFormActions.tsx +154 -29
  76. package/src/core/EntitySidePanel.tsx +19 -21
  77. package/src/core/FireCMS.tsx +9 -1
  78. package/src/core/field_configs.tsx +15 -1
  79. package/src/form/EntityForm.tsx +37 -5
  80. package/src/form/EntityFormActions.tsx +51 -9
  81. package/src/form/PropertyFieldBinding.tsx +0 -1
  82. package/src/form/components/CustomIdField.tsx +3 -1
  83. package/src/form/components/LabelWithIcon.tsx +1 -1
  84. package/src/form/components/StorageItemPreview.tsx +1 -1
  85. package/src/form/components/StorageUploadProgress.tsx +3 -3
  86. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -2
  87. package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
  88. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -2
  89. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
  90. package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
  91. package/src/form/field_bindings/SelectFieldBinding.tsx +2 -1
  92. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +216 -153
  93. package/src/form/index.tsx +1 -0
  94. package/src/hooks/data/save.ts +0 -6
  95. package/src/hooks/useBuildNavigationController.tsx +299 -96
  96. package/src/internal/useBuildSideEntityController.tsx +8 -5
  97. package/src/preview/PropertyPreview.tsx +14 -0
  98. package/src/preview/components/ImagePreview.tsx +2 -2
  99. package/src/preview/components/ReferencePreview.tsx +1 -1
  100. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
  101. package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
  102. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
  103. package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
  104. package/src/routes/FireCMSRoute.tsx +3 -3
  105. package/src/types/analytics.ts +3 -0
  106. package/src/types/collections.ts +32 -4
  107. package/src/types/customization_controller.tsx +9 -0
  108. package/src/types/entity_actions.tsx +57 -6
  109. package/src/types/firecms.tsx +11 -2
  110. package/src/types/navigation.ts +34 -6
  111. package/src/types/plugins.tsx +40 -1
  112. package/src/types/properties.ts +8 -0
  113. package/src/types/property_config.tsx +1 -0
  114. package/src/types/side_entity_controller.tsx +10 -5
  115. package/src/util/callbacks.ts +119 -0
  116. package/src/util/icons.tsx +22 -7
  117. package/src/util/index.ts +1 -0
  118. package/src/util/join_collections.ts +3 -1
  119. package/src/util/navigation_from_path.ts +15 -5
  120. package/src/util/navigation_utils.ts +72 -57
  121. package/src/util/objects.ts +53 -20
  122. package/src/util/resolutions.ts +13 -1
  123. package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
  124. package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
@@ -32,7 +32,8 @@ import {
32
32
  useAuthController,
33
33
  useCustomizationController,
34
34
  useDataSource,
35
- useFireCMSContext,
35
+ useFireCMSContext, useNavigationController,
36
+ useSideEntityController,
36
37
  useSnackbarController
37
38
  } from "../hooks";
38
39
  import { Alert, CheckIcon, Chip, cls, EditIcon, NotesIcon, paperMixin, Tooltip, Typography } from "@firecms/ui";
@@ -57,6 +58,7 @@ export type OnUpdateParams = {
57
58
 
58
59
  export type EntityFormProps<M extends Record<string, any>> = {
59
60
  path: string;
61
+ fullIdPath?: string;
60
62
  collection: EntityCollection<M>;
61
63
  entityId?: string;
62
64
  entity?: Entity<M>;
@@ -96,6 +98,7 @@ export type EntityFormProps<M extends Record<string, any>> = {
96
98
 
97
99
  export function EntityForm<M extends Record<string, any>>({
98
100
  path,
101
+ fullIdPath,
99
102
  entityId: entityIdProp,
100
103
  collection,
101
104
  onValuesModified,
@@ -123,6 +126,19 @@ export function EntityForm<M extends Record<string, any>>({
123
126
  console.warn(`The collection ${collection.path} has customId and formAutoSave enabled. This is not supported and formAutoSave will be ignored`);
124
127
  }
125
128
 
129
+
130
+ const sideEntityController = useSideEntityController();
131
+ const navigationController = useNavigationController();
132
+
133
+ const navigateBack = useCallback(() => {
134
+ if (openEntityMode === "side_panel") {
135
+ // If we are in side panel mode, we close the side panel
136
+ sideEntityController.close();
137
+ } else {
138
+ window.history.back();
139
+ }
140
+ }, []);
141
+
126
142
  const authController = useAuthController();
127
143
  const [status, setStatus] = useState<EntityStatus>(initialStatus);
128
144
 
@@ -433,14 +449,16 @@ export function EntityForm<M extends Record<string, any>>({
433
449
  const plugins = customizationController.plugins;
434
450
 
435
451
  const actionsDisabled = disabled || formex.isSubmitting || (status === "existing" && !formex.dirty) || Boolean(disabledProp);
452
+ const parentCollectionIds = navigationController.getParentCollectionIds(path);
453
+
436
454
  if (plugins && collection) {
437
455
  const actionProps: PluginFormActionProps = {
438
456
  entityId,
457
+ parentCollectionIds,
439
458
  path,
440
459
  status,
441
- collection: collection,
460
+ collection,
442
461
  context,
443
- currentEntityId: entityId,
444
462
  formContext,
445
463
  openEntityMode,
446
464
  disabled: actionsDisabled,
@@ -621,11 +639,17 @@ export function EntityForm<M extends Record<string, any>>({
621
639
  <>
622
640
  {!Builder && <div className={"w-full py-2 flex flex-col items-start my-4 lg:my-6"}>
623
641
  <Typography
624
- className={"py-4 flex-grow line-clamp-1 " + (collection.hideIdFromForm ? "mb-2" : "mb-0")}
642
+ className={"my-4 flex-grow line-clamp-1 " + (collection.hideIdFromForm ? "mb-6" : "")}
625
643
  variant={"h4"}>
626
644
  {title ?? collection.singularName ?? collection.name}
627
645
  </Typography>
628
- {showEntityPath && <Alert color={"base"} className={"w-full"} size={"small"}>
646
+
647
+ {!entity?.values && initialStatus === "existing" &&
648
+ <Alert color={"warning"} size={"small"} outerClassName={"w-full mb-4 text-xs"}>
649
+ This entity does not exist in the database
650
+ </Alert>}
651
+
652
+ {showEntityPath && <Alert color={"base"} outerClassName={"w-full"} size={"small"}>
629
653
  <code
630
654
  className={"text-xs select-all text-text-secondary dark:text-text-secondary-dark"}>
631
655
  {entity?.path ?? path}/{entityId}
@@ -635,6 +659,10 @@ export function EntityForm<M extends Record<string, any>>({
635
659
 
636
660
  {children}
637
661
 
662
+ {initialEntityId && !entity && initialStatus !== "new" && <Alert color={"info"} size={"small"}>
663
+ This entity does not exist in the database
664
+ </Alert>}
665
+
638
666
  {!Builder && !collection.hideIdFromForm &&
639
667
  <CustomIdField customId={collection.customId}
640
668
  entityId={entityId}
@@ -668,6 +696,8 @@ export function EntityForm<M extends Record<string, any>>({
668
696
  const dialogActions = <EntityFormActionsComponent
669
697
  collection={resolvedCollection}
670
698
  path={path}
699
+ fullPath={path}
700
+ fullIdPath={fullIdPath}
671
701
  entity={entity}
672
702
  layout={forceActionsAtTheBottom ? "bottom" : "side"}
673
703
  savingError={savingError}
@@ -677,6 +707,8 @@ export function EntityForm<M extends Record<string, any>>({
677
707
  pluginActions={pluginActions ?? []}
678
708
  openEntityMode={openEntityMode}
679
709
  showDefaultActions={showDefaultActions}
710
+ navigateBack={navigateBack}
711
+ formContext={formContext}
680
712
  />;
681
713
 
682
714
  return (
@@ -1,10 +1,19 @@
1
1
  import React from "react";
2
- import { Entity, EntityAction, FireCMSContext, ResolvedEntityCollection, SideEntityController } from "../types";
2
+ import {
3
+ Entity,
4
+ EntityAction,
5
+ FireCMSContext,
6
+ FormContext,
7
+ ResolvedEntityCollection,
8
+ SideEntityController
9
+ } from "../types";
3
10
  import { Button, cls, defaultBorderMixin, DialogActions, IconButton, LoadingButton, Typography } from "@firecms/ui";
4
11
  import { FormexController } from "@firecms/formex";
5
12
  import { useFireCMSContext, useSideEntityController } from "../hooks";
6
13
 
7
14
  export interface EntityFormActionsProps {
15
+ fullPath: string;
16
+ fullIdPath?: string;
8
17
  collection: ResolvedEntityCollection;
9
18
  path: string;
10
19
  entity?: Entity;
@@ -16,9 +25,13 @@ export interface EntityFormActionsProps {
16
25
  pluginActions: React.ReactNode[];
17
26
  openEntityMode: "side_panel" | "full_screen";
18
27
  showDefaultActions?: boolean;
28
+ navigateBack: () => void;
29
+ formContext: FormContext
19
30
  }
20
31
 
21
32
  export function EntityFormActions({
33
+ fullPath,
34
+ fullIdPath,
22
35
  collection,
23
36
  entity,
24
37
  layout,
@@ -27,7 +40,9 @@ export function EntityFormActions({
27
40
  disabled,
28
41
  status,
29
42
  pluginActions,
30
- openEntityMode
43
+ openEntityMode,
44
+ navigateBack,
45
+ formContext
31
46
  }: EntityFormActionsProps) {
32
47
 
33
48
  const context = useFireCMSContext();
@@ -35,6 +50,8 @@ export function EntityFormActions({
35
50
 
36
51
  return layout === "bottom"
37
52
  ? buildBottomActions({
53
+ fullPath,
54
+ fullIdPath,
38
55
  savingError,
39
56
  entity,
40
57
  collection,
@@ -44,9 +61,13 @@ export function EntityFormActions({
44
61
  disabled,
45
62
  status,
46
63
  pluginActions,
47
- openEntityMode
64
+ openEntityMode,
65
+ navigateBack,
66
+ formContext
48
67
  })
49
68
  : buildSideActions({
69
+ fullPath,
70
+ fullIdPath,
50
71
  savingError,
51
72
  entity,
52
73
  collection,
@@ -56,11 +77,15 @@ export function EntityFormActions({
56
77
  disabled,
57
78
  status,
58
79
  pluginActions,
59
- openEntityMode
80
+ openEntityMode,
81
+ navigateBack,
82
+ formContext
60
83
  });
61
84
  }
62
85
 
63
86
  type ActionsViewProps<M extends object> = {
87
+ fullPath: string,
88
+ fullIdPath?: string,
64
89
  savingError: Error | undefined,
65
90
  entity: Entity<M> | undefined,
66
91
  formActions?: EntityAction[],
@@ -72,11 +97,15 @@ type ActionsViewProps<M extends object> = {
72
97
  status: "new" | "existing" | "copy",
73
98
  pluginActions?: React.ReactNode[],
74
99
  openEntityMode: "side_panel" | "full_screen";
100
+ navigateBack: () => void;
101
+ formContext: FormContext
75
102
  };
76
103
 
77
104
  function buildBottomActions<M extends object>({
78
105
  savingError,
79
106
  entity,
107
+ fullPath,
108
+ fullIdPath,
80
109
  formActions,
81
110
  collection,
82
111
  context,
@@ -85,7 +114,9 @@ function buildBottomActions<M extends object>({
85
114
  disabled,
86
115
  status,
87
116
  pluginActions,
88
- openEntityMode
117
+ openEntityMode,
118
+ navigateBack,
119
+ formContext
89
120
  }: ActionsViewProps<M>) {
90
121
 
91
122
  return <DialogActions position={"absolute"}>
@@ -103,12 +134,16 @@ function buildBottomActions<M extends object>({
103
134
  event.stopPropagation();
104
135
  if (entity)
105
136
  action.onClick({
137
+ view: "form",
106
138
  entity,
107
- fullPath: collection.path,
139
+ fullPath: fullPath ?? collection.path,
140
+ fullIdPath: fullIdPath ?? collection.id,
108
141
  collection: collection,
109
142
  context,
110
143
  sideEntityController,
111
- openEntityMode: openEntityMode
144
+ openEntityMode: openEntityMode,
145
+ navigateBack,
146
+ formContext
112
147
  });
113
148
  }}>
114
149
  {action.icon}
@@ -116,10 +151,14 @@ function buildBottomActions<M extends object>({
116
151
  ))}
117
152
  </div>}
118
153
  {pluginActions}
119
- <Button variant="text" disabled={disabled || isSubmitting} type="reset">
154
+ <Button variant="text" disabled={disabled || isSubmitting}
155
+ color={"primary"}
156
+ type="reset">
120
157
  {status === "existing" ? "Discard" : "Clear"}
121
158
  </Button>
122
- <Button variant={"filled"} color="primary" type="submit"
159
+ <Button variant={"filled"}
160
+ color="primary"
161
+ type="submit"
123
162
  disabled={disabled || isSubmitting}>
124
163
  {status === "existing" && "Save"}
125
164
  {status === "copy" && "Create copy"}
@@ -133,6 +172,9 @@ function buildSideActions<M extends object>({
133
172
  savingError,
134
173
  entity,
135
174
  formActions,
175
+ fullPath,
176
+ fullIdPath,
177
+ openEntityMode,
136
178
  collection,
137
179
  context,
138
180
  sideEntityController,
@@ -214,7 +214,6 @@ function FieldInternal<T extends CMSType, CustomProps, M extends Record<string,
214
214
 
215
215
  const customFieldProps: any = property.customProps;
216
216
  const value = formexFieldProps.field.value;
217
- // const initialValue = formexFieldProps.meta.initialValue;
218
217
  const error = getIn(formexFieldProps.form.errors, propertyKey);
219
218
  const touched = getIn(formexFieldProps.form.touched, propertyKey);
220
219
 
@@ -103,7 +103,9 @@ export function CustomIdField<M extends Record<string, any>>({
103
103
  size={"large"}
104
104
  error={error}
105
105
  fullWidth={true}
106
- onValueChange={(v) => onChange(v as string)}
106
+ onValueChange={(v) => {
107
+ onChange(v as string);
108
+ }}
107
109
  {...fieldProps}
108
110
  renderValue={(option) => {
109
111
  const enumConfig = enumValues.find(e => e.id === option);
@@ -24,7 +24,7 @@ export const LabelWithIcon = forwardRef<HTMLDivElement, LabelWithIconProps>(
24
24
  return (
25
25
  <div
26
26
  ref={ref}
27
- className={cls("inline-flex items-center my-0.5",
27
+ className={cls("align-middle inline-flex items-center my-0.5",
28
28
  small ? "gap-1" : "gap-2",
29
29
  className)}
30
30
  >
@@ -30,7 +30,7 @@ export function StorageItemPreview({
30
30
 
31
31
  return (
32
32
  <div className={cls(paperMixin,
33
- "relative m-4 border-box flex items-center justify-center",
33
+ "relative border-box flex items-center justify-center",
34
34
  size === "large" ? "min-w-[220px] min-h-[220px] max-w-[220px]" : "min-w-[118px] min-h-[118px] max-w-[118px]",
35
35
  className)}>
36
36
 
@@ -79,7 +79,7 @@ export function StorageUploadProgress({
79
79
  }, [entry.file, entry.fileName, upload]);
80
80
 
81
81
  if (simple) {
82
- return <div className={`m-4 w-${imageSize} h-${imageSize}`}>
82
+ return <div className={`w-${imageSize} h-${imageSize}`}>
83
83
 
84
84
  {loading && <Skeleton className={`w-${imageSize} h-${imageSize}`}/>}
85
85
 
@@ -88,11 +88,11 @@ export function StorageUploadProgress({
88
88
  return (
89
89
 
90
90
  <div className={cls(paperMixin,
91
- "relative m-4 border-box flex items-center justify-center",
91
+ "p-4 relative border-box flex items-center justify-center",
92
92
  `min-w-[${imageSize}px] min-h-[${imageSize}px]`)}>
93
93
 
94
94
  {loading &&
95
- <Skeleton className="m-4 w-full h-full"/>}
95
+ <Skeleton className="w-full h-full"/>}
96
96
 
97
97
  {error && <ErrorView title={"Error uploading file"}
98
98
  error={error}/>}
@@ -6,7 +6,7 @@ import { ArrayContainer, ArrayEntryParams, ErrorView } from "../../components";
6
6
  import { getIconForProperty, getReferenceFrom } from "../../util";
7
7
 
8
8
  import { useNavigationController, useReferenceDialog } from "../../hooks";
9
- import { Button, cls, ExpandablePanel, fieldBackgroundMixin, Typography } from "@firecms/ui";
9
+ import { Button, cls, EditIcon, ExpandablePanel, fieldBackgroundMixin, Typography } from "@firecms/ui";
10
10
  import { useClearRestoreValue } from "../useClearRestoreValue";
11
11
 
12
12
  type ArrayOfReferencesFieldProps = FieldProps<EntityReference[]>;
@@ -127,11 +127,12 @@ export function ArrayOfReferencesFieldBinding({
127
127
  />
128
128
 
129
129
  <Button
130
- className="my-4 justify-center text-left"
130
+ className="ml-3.5 my-4 justify-center text-left"
131
131
  variant="text"
132
132
  color="primary"
133
133
  disabled={isSubmitting}
134
134
  onClick={onEntryClick}>
135
+ <EditIcon size={"small"}/>
135
136
  Edit {property.name}
136
137
  </Button>
137
138
  </div>}
@@ -21,6 +21,7 @@ export function MapFieldBinding({
21
21
  error,
22
22
  disabled,
23
23
  property,
24
+ partOfArray,
24
25
  minimalistView: minimalistViewProp,
25
26
  includeDescription,
26
27
  autoFocus,
@@ -51,7 +52,8 @@ export function MapFieldBinding({
51
52
  }
52
53
 
53
54
  const mapFormView = <>
54
- <div className={cls("py-1 flex flex-col space-y-2", minimalistView && property.widthPercentage !== undefined ? "mt-8" : undefined)}>
55
+ <div
56
+ className={cls("py-1 flex flex-col space-y-2", minimalistView && property.widthPercentage !== undefined ? "mt-8" : undefined)}>
55
57
  {Object.entries(mapProperties)
56
58
  .filter(([_, property]) => !isHidden(property))
57
59
  .map(([entryKey, childProperty], index) => {
@@ -81,8 +83,7 @@ export function MapFieldBinding({
81
83
  {...fieldBindingProps}/>
82
84
  </ErrorBoundary>
83
85
  </div>
84
- )
85
- ;
86
+ ) ;
86
87
  }
87
88
  )
88
89
  }
@@ -117,7 +118,7 @@ export function MapFieldBinding({
117
118
 
118
119
  <FieldHelperText includeDescription={includeDescription}
119
120
  showError={showError ?? false}
120
- error={error ? (typeof error === "string" ? error : "A property of this map has an error") : undefined}
121
+ error={error && !partOfArray ? (typeof error === "string" ? error : "A property of this map has an error") : undefined}
121
122
  disabled={disabled}
122
123
  property={property}/>
123
124
 
@@ -46,11 +46,13 @@ export function MarkdownEditorFieldBinding({
46
46
  const entityId = context.entityId;
47
47
  const path = context.path;
48
48
 
49
- // const fieldVersion = useRef(0);
50
49
  const [fieldVersion, setFieldVersion] = useState(0);
51
- const internalValue = useRef(value);
50
+ const internalValue = useRef<string | null>(value);
52
51
 
53
52
  const onContentChange = useCallback((content: string) => {
53
+ if (content === value || (value === null && content === "")) {
54
+ return;
55
+ }
54
56
  internalValue.current = content;
55
57
  setValue(content);
56
58
  }, [setValue]);
@@ -84,7 +84,7 @@ export function MultiSelectFieldBinding({
84
84
  <MultiSelect
85
85
  className={"w-full mt-2"}
86
86
  size={size}
87
- value={validValue ? value.map((v) => v.toString()) : []}
87
+ value={validValue ? value.map((v) => v?.toString()) : []}
88
88
  disabled={disabled}
89
89
  modalPopover={true}
90
90
  label={<LabelWithIconAndTooltip
@@ -0,0 +1,135 @@
1
+ import React, { useCallback, useMemo } from "react";
2
+ import { Entity, EntityCollection, EntityReference, FieldProps } from "../../types";
3
+ import { useNavigationController, useReferenceDialog } from "../../hooks";
4
+ import { ReadOnlyFieldBinding } from "./ReadOnlyFieldBinding";
5
+ import { FieldHelperText, LabelWithIconAndTooltip } from "../components";
6
+ import { ErrorView } from "../../components";
7
+ import { ReferencePreview } from "../../preview";
8
+ import { getIconForProperty, IconForView } from "../../util";
9
+ import { useClearRestoreValue } from "../useClearRestoreValue";
10
+ import { EntityPreviewContainer } from "../../components/EntityPreview";
11
+ import { cls } from "@firecms/ui";
12
+
13
+ /**
14
+ * Field that opens a reference selection dialog and stores the entity ID as a string.
15
+ *
16
+ * This is one of the internal components that get mapped natively inside forms
17
+ * and tables to the specified properties.
18
+ * @group Form fields
19
+ */
20
+ export function ReferenceAsStringFieldBinding(props: FieldProps<string>) {
21
+ if (typeof props.property.reference?.path !== "string") {
22
+ return <ReadOnlyFieldBinding {...props}/>;
23
+ }
24
+
25
+ return <ReferenceAsStringFieldBindingInternal {...props}/>;
26
+ }
27
+
28
+ function ReferenceAsStringFieldBindingInternal({
29
+ propertyKey,
30
+ value,
31
+ setValue,
32
+ error,
33
+ showError,
34
+ isSubmitting,
35
+ disabled,
36
+ minimalistView,
37
+ property,
38
+ includeDescription,
39
+ size = "medium"
40
+ }: FieldProps<string>) {
41
+ if (!property.reference?.path) {
42
+ throw new Error("Property path is required for ReferenceAsStringFieldBinding");
43
+ }
44
+
45
+ useClearRestoreValue({
46
+ property,
47
+ value,
48
+ setValue
49
+ });
50
+
51
+ const navigationController = useNavigationController();
52
+ const path = property.reference.path;
53
+ const collection: EntityCollection | undefined = useMemo(() => {
54
+ return path ? navigationController.getCollection(path) : undefined;
55
+ }, [path]);
56
+
57
+ const referenceValue: EntityReference | undefined = useMemo(() => {
58
+ if (value && path) {
59
+ return new EntityReference(value, path);
60
+ }
61
+ return undefined;
62
+ }, [value, path]);
63
+
64
+ if (!collection) {
65
+ throw Error(`Couldn't find the corresponding collection for the path: ${path}`);
66
+ }
67
+
68
+ const onSingleEntitySelected = useCallback((e: Entity<any> | null) => {
69
+ setValue(e ? e.id : null);
70
+ }, [setValue]);
71
+
72
+ const referenceDialogController = useReferenceDialog({
73
+ multiselect: false,
74
+ path: path,
75
+ collection,
76
+ onSingleEntitySelected,
77
+ selectedEntityIds: value ? [value] : undefined,
78
+ forceFilter: property.reference.forceFilter
79
+ }
80
+ );
81
+
82
+ const onEntryClick = (e: React.SyntheticEvent) => {
83
+ e.preventDefault();
84
+ referenceDialogController.open();
85
+ };
86
+
87
+ return (
88
+ <>
89
+ {!minimalistView && <LabelWithIconAndTooltip
90
+ propertyKey={propertyKey}
91
+ icon={getIconForProperty(property, "small")}
92
+ required={property.validation?.required}
93
+ title={property.name}
94
+ className={"h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"}/>}
95
+
96
+ {!collection && <ErrorView
97
+ error={"The specified collection does not exist. Check console"}/>}
98
+
99
+ {collection && <>
100
+
101
+ {referenceValue && <ReferencePreview
102
+ disabled={!path}
103
+ previewProperties={property.reference?.previewProperties}
104
+ hover={!disabled}
105
+ size={size}
106
+ onClick={disabled || isSubmitting ? undefined : onEntryClick}
107
+ reference={referenceValue}
108
+ includeEntityLink={property.reference?.includeEntityLink}
109
+ includeId={property.reference?.includeId}
110
+ />}
111
+
112
+ {!value && <div className="justify-center text-left">
113
+ <EntityPreviewContainer
114
+ className={cls("px-6 h-16 text-sm font-medium flex items-center gap-6",
115
+ disabled || isSubmitting
116
+ ? "text-surface-accent-500"
117
+ : "cursor-pointer text-surface-accent-700 dark:text-surface-accent-300 hover:bg-surface-accent-50 dark:hover:bg-surface-800 group-hover:bg-surface-accent-50 dark:group-hover:bg-surface-800")}
118
+ onClick={onEntryClick}
119
+ size={"medium"}>
120
+ <IconForView collectionOrView={collection}
121
+ className={"text-surface-300 dark:text-surface-600"}/>
122
+ {`Edit ${property.name}`.toUpperCase()}
123
+ </EntityPreviewContainer>
124
+ </div>}
125
+ </>}
126
+
127
+ <FieldHelperText includeDescription={includeDescription}
128
+ showError={showError}
129
+ error={error}
130
+ disabled={disabled}
131
+ property={property}/>
132
+
133
+ </>
134
+ );
135
+ }
@@ -67,8 +67,9 @@ export function SelectFieldBinding<T extends EnumType>({
67
67
  </PropertyIdCopyTooltip>}
68
68
  endAdornment={
69
69
  property.clearable && !disabled && <IconButton
70
+ size="small"
70
71
  onClick={handleClearClick}>
71
- <CloseIcon/>
72
+ <CloseIcon size={"small"}/>
72
73
  </IconButton>
73
74
  }
74
75
  onValueChange={(updatedValue: string) => {