@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
@@ -18,7 +18,7 @@ export function ClearFilterSortButton({
18
18
  if ((filterIsSet || sortIsSet) && (tableController.clearFilter || tableController.setSortBy)) {
19
19
  let label;
20
20
  if (filterIsSet && sortIsSet) {
21
- label = "Clear filter and sort";
21
+ label = "Clear filter/sort";
22
22
  } else if (filterIsSet) {
23
23
  label = "Clear filter";
24
24
  } else {
@@ -1,15 +1,15 @@
1
1
  import React from "react";
2
2
 
3
- import { Button, Dialog, DialogActions, DialogContent, DialogTitle, LoadingButton, Typography } from "@firecms/ui";
3
+ import { Button, Dialog, DialogActions, DialogContent, DialogTitle, LoadingButton } from "@firecms/ui";
4
4
 
5
5
  export function ConfirmationDialog({
6
- open,
7
- onAccept,
8
- onCancel,
9
- title,
10
- loading,
11
- body
12
- }: {
6
+ open,
7
+ onAccept,
8
+ onCancel,
9
+ title,
10
+ loading,
11
+ body
12
+ }: {
13
13
  open: boolean,
14
14
  onAccept: () => void,
15
15
  onCancel: () => void,
@@ -29,6 +29,7 @@ export function ConfirmationDialog({
29
29
 
30
30
  <DialogActions>
31
31
  <Button
32
+ color={"primary"}
32
33
  variant={"text"}
33
34
  onClick={onCancel}
34
35
  autoFocus>Cancel</Button>
@@ -38,7 +39,7 @@ export function ConfirmationDialog({
38
39
  type="submit"
39
40
  loading={loading}
40
41
  onClick={onAccept}
41
- >
42
+ autoFocus>
42
43
  Ok
43
44
  </LoadingButton>
44
45
  </DialogActions>
@@ -33,6 +33,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
33
33
  entity,
34
34
  collection,
35
35
  fullPath,
36
+ fullIdPath,
36
37
  width,
37
38
  frozen,
38
39
  isSelected,
@@ -50,6 +51,7 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
50
51
  entity: Entity<any>,
51
52
  collection?: EntityCollection<any>,
52
53
  fullPath?: string,
54
+ fullIdPath?: string,
53
55
  width: number,
54
56
  frozen?: boolean,
55
57
  size: CollectionSize,
@@ -105,8 +107,10 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
105
107
  onClick={(event: MouseEvent) => {
106
108
  event.stopPropagation();
107
109
  action.onClick({
110
+ view: "collection",
108
111
  entity,
109
112
  fullPath,
113
+ fullIdPath,
110
114
  collection,
111
115
  context,
112
116
  selectionController,
@@ -134,8 +138,10 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
134
138
  onClick={(e) => {
135
139
  e.stopPropagation();
136
140
  action.onClick({
141
+ view: "collection",
137
142
  entity,
138
143
  fullPath,
144
+ fullIdPath,
139
145
  collection,
140
146
  context,
141
147
  selectionController,
@@ -4,10 +4,12 @@ import {
4
4
  CMSType,
5
5
  Entity,
6
6
  EntityReference,
7
+ ReferenceProperty,
7
8
  ResolvedArrayProperty,
8
9
  ResolvedNumberProperty,
9
10
  ResolvedProperty,
10
- ResolvedStringProperty
11
+ ResolvedStringProperty,
12
+ StringProperty
11
13
  } from "../../types";
12
14
 
13
15
  import { VirtualTableInput } from "../VirtualTable/fields/VirtualTableInput";
@@ -251,7 +253,27 @@ export const PropertyTableCell = React.memo<PropertyTableCellProps<any>>(
251
253
 
252
254
  if (!customField && (!customPreview || selected)) {
253
255
  const isAStorageProperty = isStorageProperty(property);
254
- if (isAStorageProperty) {
256
+ if (property.dataType === "string" && (property as StringProperty).reference?.path) {
257
+ const stringProperty = property as StringProperty;
258
+ const path = stringProperty.reference?.path as string;
259
+ const referenceProperty = stringProperty.reference as ReferenceProperty;
260
+ const referenceValue = internalValue ? new EntityReference(internalValue, path) : undefined;
261
+ innerComponent =
262
+ <TableReferenceField name={propertyKey as string}
263
+ internalValue={referenceValue}
264
+ updateValue={(v) => updateValue(v ? (v as EntityReference).id : null)}
265
+ disabled={disabled}
266
+ size={size}
267
+ path={path}
268
+ multiselect={false}
269
+ previewProperties={referenceProperty.previewProperties}
270
+ includeId={referenceProperty.includeId}
271
+ includeEntityLink={referenceProperty.includeEntityLink}
272
+ title={stringProperty.name}
273
+ forceFilter={referenceProperty.forceFilter}
274
+ />;
275
+ allowScroll = false;
276
+ } else if (isAStorageProperty) {
255
277
  innerComponent = <TableStorageUpload error={validationError ?? error}
256
278
  disabled={disabled}
257
279
  focused={selected}
@@ -310,7 +332,7 @@ export const PropertyTableCell = React.memo<PropertyTableCellProps<any>>(
310
332
  updateValue={updateValue}
311
333
  />;
312
334
  fullHeight = true;
313
- } else if (stringProperty.markdown || !stringProperty.storage) {
335
+ } else if (stringProperty.markdown || !stringProperty.storage || !stringProperty.reference) {
314
336
  const multiline = Boolean(stringProperty.multiline) || Boolean(stringProperty.markdown);
315
337
  innerComponent = <VirtualTableInput error={validationError ?? error}
316
338
  disabled={disabled}
@@ -394,7 +416,7 @@ export const PropertyTableCell = React.memo<PropertyTableCellProps<any>>(
394
416
  size={size}
395
417
  multiselect={true}
396
418
  path={arrayProperty.of.path}
397
- previewProperties={arrayProperty.of.previewProperties}
419
+ previewProperties={arrayProperty.of.previewKeys}
398
420
  title={arrayProperty.name}
399
421
  forceFilter={arrayProperty.of.forceFilter}
400
422
  includeId={arrayProperty.of.includeId}
@@ -55,7 +55,7 @@ export function CollectionTableToolbar({
55
55
  <Tooltip title={"Table row size"} side={"right"} sideOffset={4}>
56
56
  <Select
57
57
  value={size as string}
58
- className="w-16 h-10"
58
+ className="w-16 ml-2"
59
59
  size={"small"}
60
60
  onValueChange={(v) => onSizeChanged(v as CollectionSize)}
61
61
  renderValue={(v) => <div className={"font-medium"}>{v.toUpperCase()}</div>}
@@ -73,7 +73,7 @@ export function CollectionTableToolbar({
73
73
  <div
74
74
  className={cls(defaultBorderMixin, "no-scrollbar min-h-[56px] overflow-x-auto px-2 md:px-4 bg-surface-50 dark:bg-surface-900 border-b flex flex-row justify-between items-center w-full")}>
75
75
 
76
- <div className="flex items-center gap-2 md:mr-4 mr-2">
76
+ <div className="flex items-center gap-1 md:mr-4 mr-2">
77
77
 
78
78
  {title && <div className={"hidden lg:block"}>
79
79
  {title}
@@ -85,7 +85,7 @@ export function CollectionTableToolbar({
85
85
 
86
86
  </div>
87
87
 
88
- <div className="flex items-center gap-2">
88
+ <div className="flex items-center gap-1">
89
89
 
90
90
  {largeLayout && <div className="w-[22px] mr-4">
91
91
  {loading &&
@@ -5,6 +5,7 @@ import useMeasure from "react-use-measure";
5
5
  import { cls, DoNotDisturbOnIcon, Tooltip } from "@firecms/ui";
6
6
  import { ErrorBoundary } from "../../../components";
7
7
  import { getRowHeight, TableSize } from "../../common/table_height";
8
+ import { ErrorTooltip } from "../../ErrorTooltip";
8
9
 
9
10
  interface EntityTableCellProps {
10
11
  children: React.ReactNode;
@@ -183,7 +184,7 @@ export const EntityTableCell = React.memo<EntityTableCellProps>(
183
184
  ? "border-primary"
184
185
  : "border-transparent";
185
186
 
186
- return (
187
+ const result = <>
187
188
  <div
188
189
  className={cls(
189
190
  "transition-colors duration-100 ease-in-out",
@@ -241,7 +242,17 @@ export const EntityTableCell = React.memo<EntityTableCellProps>(
241
242
  </div>}
242
243
 
243
244
  </div>
244
- );
245
+ </>;
246
+ if (showError) {
247
+ return (
248
+ <ErrorTooltip
249
+ align={"start"}
250
+ title={error?.message ?? "Error"}>
251
+ {result}
252
+ </ErrorTooltip>
253
+ );
254
+ }
255
+ return result;
245
256
  }, (a, b) => {
246
257
  return a.error === b.error &&
247
258
  a.value === b.value &&
@@ -17,7 +17,6 @@ import { Formex, useCreateFormex } from "@firecms/formex";
17
17
  import { useDraggable } from "./useDraggable";
18
18
  import { CustomFieldValidator, getYupEntitySchema } from "../../../../form/validation";
19
19
  import { useWindowSize } from "./useWindowSize";
20
- import { ElementResizeListener } from "./ElementResizeListener";
21
20
  import { getPropertyInPath, isReadOnly, resolveCollection } from "../../../../util";
22
21
  import { Button, CloseIcon, DialogActions, IconButton, Typography } from "@firecms/ui";
23
22
  import { PropertyFieldBinding, yupToFormErrors } from "../../../../form";
@@ -71,7 +70,7 @@ export function PopupFormFieldLoading<M extends Record<string, any>>({
71
70
  collection: inputCollection
72
71
  }).then(setEntity);
73
72
  }
74
- }, [entityId, inputCollection]);
73
+ }, [entityId, inputCollection, dataSource, path]);
75
74
 
76
75
  if (!entity) return null;
77
76
  return <PopupFormFieldInternal {...{
@@ -147,33 +146,35 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
147
146
  };
148
147
  }, [cellRect, windowSize.height, windowSize.width]);
149
148
 
150
- const normalizePosition = useCallback(({
151
- x,
152
- y
153
- }: { x: number, y: number }) => {
154
-
155
- const draggableBoundingRect = draggableRef.current?.getBoundingClientRect();
156
- if (!draggableBoundingRect)
157
- throw Error("normalizePosition called before draggableBoundingRect is set");
158
-
149
+ const normalizePosition = useCallback((
150
+ pos: { x: number, y: number },
151
+ draggableBoundingRect: DOMRect,
152
+ currentWindowSize: { width: number, height: number }
153
+ ) => {
154
+ if (!draggableBoundingRect || draggableBoundingRect.width === 0 || draggableBoundingRect.height === 0) {
155
+ return pos;
156
+ }
159
157
  return {
160
- x: Math.max(0, Math.min(x, windowSize.width - draggableBoundingRect.width)),
161
- y: Math.max(0, Math.min(y, windowSize.height - draggableBoundingRect.height))
158
+ x: Math.max(0, Math.min(pos.x, currentWindowSize.width - draggableBoundingRect.width)),
159
+ y: Math.max(0, Math.min(pos.y, currentWindowSize.height - draggableBoundingRect.height))
162
160
  };
163
- }, [windowSize]);
161
+ }, []);
164
162
 
165
- const updatePopupLocation = useCallback((position?: {
163
+ const updatePopupLocation = useCallback((newPositionCandidate?: {
166
164
  x: number,
167
165
  y: number
168
166
  }) => {
169
-
170
167
  const draggableBoundingRect = draggableRef.current?.getBoundingClientRect();
171
- if (!cellRect || !draggableBoundingRect) return;
172
- // const newPosition = normalizePosition(position ?? getInitialLocation());
173
- const newPosition = position ?? normalizePosition(getInitialLocation());
174
- if (!popupLocation || newPosition.x !== popupLocation.x || newPosition.y !== popupLocation.y)
175
- setPopupLocation(newPosition);
176
- }, [cellRect, getInitialLocation, normalizePosition, popupLocation]);
168
+ if (!cellRect || !draggableBoundingRect || draggableBoundingRect.width === 0 || draggableBoundingRect.height === 0) {
169
+ return;
170
+ }
171
+ const basePosition = newPositionCandidate ?? getInitialLocation();
172
+ const newNormalizedPosition = normalizePosition(basePosition, draggableBoundingRect, windowSize);
173
+
174
+ if (!popupLocation || newNormalizedPosition.x !== popupLocation.x || newNormalizedPosition.y !== popupLocation.y) {
175
+ setPopupLocation(newNormalizedPosition);
176
+ }
177
+ }, [cellRect, getInitialLocation, normalizePosition, popupLocation, windowSize]);
177
178
 
178
179
  useDraggable({
179
180
  containerRef: draggableRef,
@@ -192,19 +193,25 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
192
193
 
193
194
  useLayoutEffect(
194
195
  () => {
196
+ if (!cellRect || initialPositionSet.current) return;
197
+ // Ensure draggableRef is available and has dimensions before initial positioning
195
198
  const draggableBoundingRect = draggableRef.current?.getBoundingClientRect();
196
- if (!cellRect || !draggableBoundingRect || initialPositionSet.current) return;
199
+ if (!draggableBoundingRect || draggableBoundingRect.width === 0 || draggableBoundingRect.height === 0) {
200
+ // If not ready, perhaps wait or log. For now, just return.
201
+ // This might need a retry mechanism or ensure content is rendered first.
202
+ return;
203
+ }
197
204
  updatePopupLocation();
198
205
  initialPositionSet.current = true;
199
206
  },
200
- [cellRect, updatePopupLocation, initialPositionSet.current]
207
+ [cellRect, updatePopupLocation] // Removed initialPositionSet.current from deps as it's a ref
201
208
  );
202
209
 
203
210
  useLayoutEffect(
204
211
  () => {
205
212
  updatePopupLocation(popupLocation);
206
213
  },
207
- [windowSize, cellRect]
214
+ [windowSize, cellRect, updatePopupLocation, popupLocation]
208
215
  );
209
216
 
210
217
  const validationSchema = useMemo(() => {
@@ -218,9 +225,28 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
218
225
  }, [collection, entityId, propertyKey, customFieldValidator]);
219
226
 
220
227
  const adaptResize = useCallback(() => {
228
+ // When the popup resizes, we want to re-evaluate its position
229
+ // based on its current location and new dimensions.
221
230
  return updatePopupLocation(popupLocation);
222
231
  }, [popupLocation, updatePopupLocation]);
223
232
 
233
+ // Setup ResizeObserver
234
+ useEffect(() => {
235
+ const element = draggableRef.current;
236
+ if (!element) return;
237
+
238
+ const observer = new ResizeObserver(() => {
239
+ adaptResize();
240
+ });
241
+
242
+ observer.observe(element);
243
+
244
+ return () => {
245
+ observer.unobserve(element);
246
+ observer.disconnect();
247
+ };
248
+ }, [adaptResize, draggableRef]);
249
+
224
250
  const saveValue = async (values: M) => {
225
251
  setSavingError(null);
226
252
  if (inputCollection && entity && onCellValueChange && propertyKey) {
@@ -288,7 +314,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
288
314
  underlyingValueHasChanged: false,
289
315
  context: formContext,
290
316
  partOfArray: false,
291
- minimalistView: false,
317
+ minimalistView: true,
292
318
  autoFocus: open
293
319
  }
294
320
  : undefined;
@@ -372,7 +398,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
372
398
  } cursor-grab overflow-visible`}
373
399
  ref={draggableRef}>
374
400
 
375
- <ElementResizeListener onResize={adaptResize}/>
401
+ {/* ElementResizeListener removed from here */}
376
402
 
377
403
  <div
378
404
  className="overflow-hidden">
@@ -1,9 +1,8 @@
1
- import React, { useEffect } from "react";
1
+ import React, { useCallback, useEffect } from "react";
2
2
 
3
3
  interface DraggableProps {
4
4
  containerRef: React.RefObject<HTMLDivElement>,
5
5
  innerRef: React.RefObject<HTMLDivElement>,
6
- // ref: React.RefObject<HTMLDivElement>,
7
6
  x?: number;
8
7
  y?: number;
9
8
  onMove: (params: { x: number, y: number }) => void,
@@ -22,7 +21,7 @@ export function useDraggable({
22
21
 
23
22
  const listeningRef = React.useRef(false);
24
23
 
25
- const onMouseDown = (event: any) => {
24
+ const onMouseDown = useCallback((event: any) => {
26
25
  if (event.button !== 0 || !containerRef.current || event.defaultPrevented || event.innerClicked) {
27
26
  return;
28
27
  }
@@ -39,16 +38,17 @@ export function useDraggable({
39
38
  document.addEventListener("selectstart", onSelect);
40
39
  listeningRef.current = true;
41
40
  // event.stopPropagation();
42
- };
43
- const onMouseDownInner = (event: any) => {
41
+ }, [containerRef, onMove]);
42
+
43
+ const onMouseDownInner = useCallback((event: any) => {
44
44
  // @ts-ignore
45
45
  event.innerClicked = true;
46
- };
46
+ }, [])
47
47
 
48
- const onSelect = (event: any) => {
48
+ const onSelect = useCallback((event: any) => {
49
49
  event.preventDefault()
50
50
  event.stopPropagation();
51
- };
51
+ }, [])
52
52
 
53
53
  const onMouseUp = (event: any) => {
54
54
  document.removeEventListener("mousemove", onMouseMove);
@@ -92,6 +92,6 @@ export function useDraggable({
92
92
  if (innerCurrent)
93
93
  innerCurrent.removeEventListener("mousedown", onMouseDownInner);
94
94
  };
95
- });
95
+ }, [containerRef, innerRef, onMouseDownInner, onMouseDown]);
96
96
 
97
97
  }
@@ -31,6 +31,7 @@ import {
31
31
  mergeEntityActions,
32
32
  navigateToEntity,
33
33
  resolveCollection,
34
+ resolveEntityAction,
34
35
  resolveProperty
35
36
  } from "../../util";
36
37
  import { ReferencePreview } from "../../preview";
@@ -151,7 +152,6 @@ export const EntityCollectionView = React.memo(
151
152
  }: EntityCollectionViewProps<M>
152
153
  ) {
153
154
 
154
-
155
155
  const context = useFireCMSContext();
156
156
  const navigation = useNavigationController();
157
157
  const fullPath = fullPathProp ?? collectionProp.path;
@@ -256,7 +256,6 @@ export const EntityCollectionView = React.memo(
256
256
  }, [unselectNavigatedEntity, sideEntityController]);
257
257
 
258
258
  const onNewClick = useCallback(() => {
259
-
260
259
  const collection = collectionRef.current;
261
260
  analyticsController.onAnalyticsEvent?.("new_entity_click", {
262
261
  path: fullPath
@@ -425,6 +424,7 @@ export const EntityCollectionView = React.memo(
425
424
  Builder: ({ entity }) => (
426
425
  <Button color={"primary"}
427
426
  variant={"outlined"}
427
+ className={"max-w-full truncate justify-start"}
428
428
  startIcon={<KeyboardTabIcon size={"small"}/>}
429
429
  onClick={(event: any) => {
430
430
  event.stopPropagation();
@@ -521,10 +521,13 @@ export const EntityCollectionView = React.memo(
521
521
  }) => {
522
522
 
523
523
  const isSelected = Boolean(usedSelectionController.selectedEntities.find(e => e.id == entity.id && e.path == entity.path));
524
+ const customEntityActions = (collection.entityActions ?? [])
525
+ .map(action => resolveEntityAction(action, customizationController.entityActions))
526
+ .filter(Boolean) as EntityAction[];
524
527
 
525
528
  const actions = getActionsForEntity({
526
529
  entity,
527
- customEntityActions: collection.entityActions
530
+ customEntityActions
528
531
  });
529
532
 
530
533
  return (
@@ -538,7 +541,8 @@ export const EntityCollectionView = React.memo(
538
541
  highlightEntity={setHighlightedEntity}
539
542
  unhighlightEntity={unselectNavigatedEntity}
540
543
  collection={collection}
541
- fullPath={fullIdPath ?? fullPath}
544
+ fullPath={fullPath}
545
+ fullIdPath={fullIdPath}
542
546
  actions={actions}
543
547
  hideId={collection?.hideIdFromCollection}
544
548
  onCollectionChange={updateLastDeleteTimestamp}
@@ -749,10 +753,13 @@ export const EntityCollectionView = React.memo(
749
753
  equal(a.defaultSize, b.defaultSize) &&
750
754
  equal(a.initialFilter, b.initialFilter) &&
751
755
  equal(a.initialSort, b.initialSort) &&
756
+ equal(a.includeJsonView, b.includeJsonView) &&
752
757
  equal(a.textSearchEnabled, b.textSearchEnabled) &&
753
758
  equal(a.additionalFields, b.additionalFields) &&
754
759
  equal(a.sideDialogWidth, b.sideDialogWidth) &&
755
760
  equal(a.openEntityMode, b.openEntityMode) &&
761
+ equal(a.exportable, b.exportable) &&
762
+ equal(a.history, b.history) &&
756
763
  equal(a.forceFilter, b.forceFilter);
757
764
  }) as React.FunctionComponent<EntityCollectionViewProps<any>>
758
765
 
@@ -71,7 +71,7 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
71
71
  ? <Button
72
72
  variant={"text"}
73
73
  disabled={!(selectedEntities?.length) || !multipleDeleteEnabled}
74
- startIcon={<DeleteIcon/>}
74
+ startIcon={<DeleteIcon size={"small"}/>}
75
75
  onClick={onMultipleDeleteClick}
76
76
  color={"primary"}
77
77
  className="lg:w-20"
@@ -79,10 +79,11 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
79
79
  ({selectedEntities?.length})
80
80
  </Button>
81
81
  : <IconButton
82
+ size={"small"}
82
83
  color={"primary"}
83
84
  disabled={!(selectedEntities?.length) || !multipleDeleteEnabled}
84
85
  onClick={onMultipleDeleteClick}>
85
- <DeleteIcon/>
86
+ <DeleteIcon size={"small"}/>
86
87
  </IconButton>;
87
88
  multipleDeleteButton =
88
89
  <Tooltip
@@ -7,6 +7,7 @@ import {
7
7
  getEntityImagePreviewPropertyKey,
8
8
  getEntityPreviewKeys,
9
9
  getEntityTitlePropertyKey,
10
+ getPropertyInPath,
10
11
  getValueInPath,
11
12
  IconForView,
12
13
  resolveCollection
@@ -26,11 +27,13 @@ export type EntityPreviewProps = {
26
27
  actions?: React.ReactNode,
27
28
  collection?: EntityCollection,
28
29
  hover?: boolean;
29
- previewProperties?: string[],
30
+ previewKeys?: string[],
30
31
  disabled?: boolean,
31
32
  entity: Entity<any>,
32
33
  includeId?: boolean,
34
+ includeTitle?: boolean,
33
35
  includeEntityLink?: boolean,
36
+ includeImage?: boolean,
34
37
  onClick?: (e: React.SyntheticEvent) => void;
35
38
  };
36
39
 
@@ -43,12 +46,14 @@ export function EntityPreview({
43
46
  disabled,
44
47
  hover,
45
48
  collection: collectionProp,
46
- previewProperties,
49
+ previewKeys,
47
50
  onClick,
48
51
  size,
49
52
  includeId = true,
53
+ includeTitle = true,
50
54
  includeEntityLink = true,
51
- entity
55
+ includeImage = true,
56
+ entity,
52
57
  }: EntityPreviewProps) {
53
58
 
54
59
  const authController = useAuthController();
@@ -72,11 +77,11 @@ export function EntityPreview({
72
77
  authController
73
78
  }), [collection]);
74
79
 
75
- const listProperties = useMemo(() => getEntityPreviewKeys(authController, resolvedCollection, customizationController.propertyConfigs, previewProperties, size === "medium" || size === "large" ? 3 : 1),
76
- [previewProperties, resolvedCollection, size]);
80
+ const listProperties = useMemo(() => previewKeys ?? getEntityPreviewKeys(authController, resolvedCollection, customizationController.propertyConfigs, previewKeys, size === "medium" || size === "large" ? 3 : 1),
81
+ [previewKeys, resolvedCollection, size]);
77
82
 
78
- const titleProperty = getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs);
79
- const imagePropertyKey = getEntityImagePreviewPropertyKey(resolvedCollection);
83
+ const titleProperty = includeTitle ? getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs) : undefined;
84
+ const imagePropertyKey = includeImage ? getEntityImagePreviewPropertyKey(resolvedCollection) : undefined;
80
85
  const imageProperty = imagePropertyKey ? resolvedCollection.properties[imagePropertyKey] : undefined;
81
86
  const usedImageProperty = imageProperty && "of" in imageProperty ? imageProperty.of : imageProperty;
82
87
  const restProperties = listProperties.filter(p => p !== titleProperty && p !== imagePropertyKey);
@@ -103,9 +108,7 @@ export function EntityPreview({
103
108
  </div>
104
109
 
105
110
 
106
- <div className={"flex flex-col grow-1 w-full m-1 shrink-1"} style={{
107
- "maxWidth": "calc(100% - 96px)"
108
- }}>
111
+ <div className={"flex flex-col grow w-full m-1 shrink min-w-0"}>
109
112
 
110
113
  {size !== "small" && includeId && (
111
114
  entity
@@ -135,9 +138,10 @@ export function EntityPreview({
135
138
  )}
136
139
 
137
140
  {restProperties && restProperties.map((key) => {
138
- const childProperty = resolvedCollection.properties[key as string];
141
+ const childProperty = getPropertyInPath(resolvedCollection.properties, key);
139
142
  if (!childProperty) return null;
140
143
 
144
+ const valueInPath = getValueInPath(entity.values, key);
141
145
  return (
142
146
  <div key={"ref_prev_" + key}
143
147
  className={cls("truncate", restProperties.length > 1 ? "my-0.5" : "my-0")}>
@@ -145,7 +149,7 @@ export function EntityPreview({
145
149
  entity
146
150
  ? <PropertyPreview
147
151
  propertyKey={key as string}
148
- value={getValueInPath(entity.values, key)}
152
+ value={valueInPath}
149
153
  property={childProperty as ResolvedProperty}
150
154
  size={"small"}/>
151
155
  : <SkeletonPropertyComponent
@@ -162,7 +166,7 @@ export function EntityPreview({
162
166
  <Tooltip title={`See details for ${entity.id}`} className={"shrink-0"}>
163
167
  <IconButton
164
168
  color={"inherit"}
165
- size={"medium"}
169
+ size={"small"}
166
170
  className={size !== "small" ? "self-start" : ""}
167
171
  onClick={(e) => {
168
172
  e.stopPropagation();
@@ -177,7 +181,7 @@ export function EntityPreview({
177
181
  updateUrl: true
178
182
  });
179
183
  }}>
180
- <KeyboardTabIcon size={"medium"}/>
184
+ <KeyboardTabIcon size={"small"}/>
181
185
  </IconButton>
182
186
  </Tooltip>}
183
187
 
@@ -26,7 +26,7 @@ export function ErrorView({
26
26
  tooltip
27
27
  }: ErrorViewProps): React.ReactElement {
28
28
  const component = error instanceof Error ? error.message : error;
29
- console.warn("ErrorView", error)
29
+ // console.warn("ErrorView", error)
30
30
 
31
31
  const body = (
32
32
  <div