@firecms/core 3.0.0-beta.12 → 3.0.0-beta.14

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 (120) hide show
  1. package/dist/app/Scaffold.d.ts +4 -0
  2. package/dist/components/ArrayContainer.d.ts +13 -11
  3. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
  4. package/dist/components/EntityJsonPreview.d.ts +3 -0
  5. package/dist/components/EntityPreview.d.ts +4 -2
  6. package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
  7. package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
  8. package/dist/components/index.d.ts +1 -0
  9. package/dist/core/DefaultAppBar.d.ts +8 -2
  10. package/dist/core/EntityEditView.d.ts +8 -1
  11. package/dist/core/FireCMS.d.ts +2 -2
  12. package/dist/form/EntityForm.d.ts +5 -1
  13. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  14. package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
  15. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
  16. package/dist/hooks/data/save.d.ts +1 -1
  17. package/dist/hooks/useBuildNavigationController.d.ts +2 -9
  18. package/dist/index.es.js +9251 -8721
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.umd.js +16686 -16160
  21. package/dist/index.umd.js.map +1 -1
  22. package/dist/internal/useBuildDataSource.d.ts +3 -2
  23. package/dist/internal/useBuildSideEntityController.d.ts +3 -3
  24. package/dist/types/collections.d.ts +26 -2
  25. package/dist/types/fields.d.ts +4 -1
  26. package/dist/types/firecms.d.ts +3 -2
  27. package/dist/types/navigation.d.ts +9 -0
  28. package/dist/types/plugins.d.ts +13 -0
  29. package/dist/types/properties.d.ts +22 -1
  30. package/dist/types/side_entity_controller.d.ts +4 -0
  31. package/dist/util/callbacks.d.ts +2 -0
  32. package/dist/util/createFormexStub.d.ts +2 -0
  33. package/dist/util/index.d.ts +1 -0
  34. package/dist/util/navigation_utils.d.ts +2 -1
  35. package/dist/util/objects.d.ts +1 -1
  36. package/dist/util/property_utils.d.ts +2 -2
  37. package/dist/util/references.d.ts +2 -2
  38. package/dist/util/resolutions.d.ts +11 -6
  39. package/package.json +9 -6
  40. package/src/app/Scaffold.tsx +13 -3
  41. package/src/components/ArrayContainer.tsx +414 -282
  42. package/src/components/ClearFilterSortButton.tsx +1 -1
  43. package/src/components/ConfirmationDialog.tsx +9 -9
  44. package/src/components/DeleteEntityDialog.tsx +4 -2
  45. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +8 -6
  46. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +1 -1
  47. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +1 -1
  48. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
  49. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +58 -29
  50. package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
  51. package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -6
  52. package/src/components/EntityJsonPreview.tsx +66 -0
  53. package/src/components/EntityPreview.tsx +27 -16
  54. package/src/components/EntityView.tsx +4 -1
  55. package/src/components/ErrorView.tsx +1 -1
  56. package/src/components/HomePage/DefaultHomePage.tsx +2 -1
  57. package/src/components/HomePage/NavigationCardBinding.tsx +3 -1
  58. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -1
  59. package/src/components/SelectableTable/SelectableTable.tsx +140 -143
  60. package/src/components/VirtualTable/VirtualTable.tsx +37 -31
  61. package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
  62. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
  63. package/src/components/common/default_entity_actions.tsx +15 -4
  64. package/src/components/index.tsx +2 -0
  65. package/src/core/DefaultAppBar.tsx +17 -5
  66. package/src/core/EntityEditView.tsx +135 -47
  67. package/src/core/EntitySidePanel.tsx +15 -20
  68. package/src/core/FireCMS.tsx +19 -12
  69. package/src/form/EntityForm.tsx +32 -24
  70. package/src/form/PropertyFieldBinding.tsx +8 -6
  71. package/src/form/components/CustomIdField.tsx +3 -1
  72. package/src/form/components/LabelWithIcon.tsx +1 -1
  73. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +4 -1
  74. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -2
  75. package/src/form/field_bindings/BlockFieldBinding.tsx +1 -1
  76. package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
  77. package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
  78. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +6 -2
  79. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +3 -3
  80. package/src/form/field_bindings/RepeatFieldBinding.tsx +9 -2
  81. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -2
  82. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +227 -156
  83. package/src/hooks/data/save.ts +24 -35
  84. package/src/hooks/useBuildNavigationController.tsx +42 -17
  85. package/src/hooks/useFireCMSContext.tsx +0 -30
  86. package/src/internal/useBuildDataSource.ts +9 -5
  87. package/src/internal/useBuildSideEntityController.tsx +26 -20
  88. package/src/preview/PropertyPreview.tsx +4 -2
  89. package/src/preview/components/ImagePreview.tsx +2 -2
  90. package/src/preview/components/ReferencePreview.tsx +1 -1
  91. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +5 -4
  92. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +4 -3
  93. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +4 -2
  94. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -3
  95. package/src/preview/property_previews/ArrayOneOfPreview.tsx +4 -2
  96. package/src/preview/property_previews/ArrayPropertyPreview.tsx +4 -2
  97. package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
  98. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
  99. package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
  100. package/src/routes/FireCMSRoute.tsx +15 -4
  101. package/src/types/collections.ts +31 -2
  102. package/src/types/fields.tsx +5 -1
  103. package/src/types/firecms.tsx +3 -3
  104. package/src/types/navigation.ts +11 -0
  105. package/src/types/plugins.tsx +17 -0
  106. package/src/types/properties.ts +27 -1
  107. package/src/types/side_entity_controller.tsx +5 -0
  108. package/src/util/callbacks.ts +119 -0
  109. package/src/util/createFormexStub.tsx +62 -0
  110. package/src/util/index.ts +1 -0
  111. package/src/util/join_collections.ts +3 -1
  112. package/src/util/navigation_from_path.ts +5 -1
  113. package/src/util/navigation_utils.ts +84 -20
  114. package/src/util/objects.ts +54 -17
  115. package/src/util/property_utils.tsx +7 -3
  116. package/src/util/references.ts +8 -6
  117. package/src/util/resolutions.ts +17 -9
  118. package/src/util/useStorageUploadController.tsx +21 -2
  119. package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
  120. 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,
@@ -38,7 +38,7 @@ export function ConfirmationDialog({
38
38
  type="submit"
39
39
  loading={loading}
40
40
  onClick={onAccept}
41
- >
41
+ autoFocus>
42
42
  Ok
43
43
  </LoadingButton>
44
44
  </DialogActions>
@@ -3,6 +3,7 @@ import React, { useCallback, useMemo, useState } from "react";
3
3
  import { Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle } from "@firecms/ui";
4
4
  import {
5
5
  deleteEntityWithCallbacks,
6
+ useAuthController,
6
7
  useCustomizationController,
7
8
  useDataSource,
8
9
  useFireCMSContext,
@@ -34,7 +35,7 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
34
35
  onMultipleEntitiesDelete,
35
36
  path
36
37
  }: DeleteEntityDialogProps<M>) {
37
-
38
+ const authController = useAuthController();
38
39
  const dataSource = useDataSource(collection);
39
40
  const customizationController = useCustomizationController();
40
41
  const snackbarController = useSnackbarController();
@@ -50,7 +51,8 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
50
51
  const resolvedCollection = useMemo(() => resolveCollection<M>({
51
52
  collection,
52
53
  path,
53
- propertyConfigs: customizationController.propertyConfigs
54
+ propertyConfigs: customizationController.propertyConfigs,
55
+ authController
54
56
  }), [collection, path]);
55
57
 
56
58
  const handleCancel = useCallback(() => {
@@ -165,19 +165,21 @@ export const EntityCollectionRowActions = function EntityCollectionRowActions({
165
165
 
166
166
  {!hideId && size !== "xs" && (
167
167
  <div
168
- className="w-[138px] overflow-hidden truncate font-mono text-xs text-text-secondary dark:text-text-secondary-dark max-w-full text-ellipsis px-2 align-center flex items-center justify-center gap-1"
168
+ className="w-[138px] overflow-hidden truncate font-mono text-xs text-text-secondary dark:text-text-secondary-dark max-w-full text-ellipsis px-2 align-center justify-center flex items-center gap-1"
169
169
  onClick={(event) => {
170
170
  event.stopPropagation();
171
171
  }}>
172
- {hasDraft && <Tooltip title={"Unsaved changes"} className={"inline"}>
172
+ {hasDraft && <Tooltip title={"Local unsaved changes"} className={"inline"}>
173
173
  <Chip colorScheme={"orangeDarker"} className={"p-0.5"}>
174
174
  <EditIcon size={12}/>
175
175
  </Chip>
176
176
  </Tooltip>}
177
- {entity
178
- ? entity.id
179
- : <Skeleton/>
180
- }
177
+ <span className="min-w-0 truncate text-center">
178
+ {entity
179
+ ? entity.id
180
+ : <Skeleton/>
181
+ }
182
+ </span>
181
183
  </div>
182
184
  )}
183
185
 
@@ -394,7 +394,7 @@ export const PropertyTableCell = React.memo<PropertyTableCellProps<any>>(
394
394
  size={size}
395
395
  multiselect={true}
396
396
  path={arrayProperty.of.path}
397
- previewProperties={arrayProperty.of.previewProperties}
397
+ previewProperties={arrayProperty.of.previewKeys}
398
398
  title={arrayProperty.name}
399
399
  forceFilter={arrayProperty.of.forceFilter}
400
400
  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>}
@@ -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,11 +17,10 @@ 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";
24
- import { useCustomizationController, useDataSource, useFireCMSContext } from "../../../../hooks";
23
+ import { useAuthController, useCustomizationController, useDataSource, useFireCMSContext } from "../../../../hooks";
25
24
  import { OnCellValueChangeParams } from "../../../common";
26
25
 
27
26
  interface PopupFormFieldProps<M extends Record<string, any>> {
@@ -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 {...{
@@ -107,6 +106,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
107
106
  }) {
108
107
 
109
108
  const fireCMSContext = useFireCMSContext();
109
+ const authController = useAuthController();
110
110
  const customizationController = useCustomizationController();
111
111
 
112
112
  const [savingError, setSavingError] = React.useState<any>();
@@ -121,7 +121,8 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
121
121
  path,
122
122
  values: entity?.values,
123
123
  entityId,
124
- propertyConfigs: customizationController.propertyConfigs
124
+ propertyConfigs: customizationController.propertyConfigs,
125
+ authController
125
126
  })
126
127
  : undefined;
127
128
 
@@ -145,33 +146,35 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
145
146
  };
146
147
  }, [cellRect, windowSize.height, windowSize.width]);
147
148
 
148
- const normalizePosition = useCallback(({
149
- x,
150
- y
151
- }: { x: number, y: number }) => {
152
-
153
- const draggableBoundingRect = draggableRef.current?.getBoundingClientRect();
154
- if (!draggableBoundingRect)
155
- throw Error("normalizePosition called before draggableBoundingRect is set");
156
-
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
+ }
157
157
  return {
158
- x: Math.max(0, Math.min(x, windowSize.width - draggableBoundingRect.width)),
159
- 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))
160
160
  };
161
- }, [windowSize]);
161
+ }, []);
162
162
 
163
- const updatePopupLocation = useCallback((position?: {
163
+ const updatePopupLocation = useCallback((newPositionCandidate?: {
164
164
  x: number,
165
165
  y: number
166
166
  }) => {
167
-
168
167
  const draggableBoundingRect = draggableRef.current?.getBoundingClientRect();
169
- if (!cellRect || !draggableBoundingRect) return;
170
- // const newPosition = normalizePosition(position ?? getInitialLocation());
171
- const newPosition = position ?? normalizePosition(getInitialLocation());
172
- if (!popupLocation || newPosition.x !== popupLocation.x || newPosition.y !== popupLocation.y)
173
- setPopupLocation(newPosition);
174
- }, [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]);
175
178
 
176
179
  useDraggable({
177
180
  containerRef: draggableRef,
@@ -190,19 +193,25 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
190
193
 
191
194
  useLayoutEffect(
192
195
  () => {
196
+ if (!cellRect || initialPositionSet.current) return;
197
+ // Ensure draggableRef is available and has dimensions before initial positioning
193
198
  const draggableBoundingRect = draggableRef.current?.getBoundingClientRect();
194
- 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
+ }
195
204
  updatePopupLocation();
196
205
  initialPositionSet.current = true;
197
206
  },
198
- [cellRect, updatePopupLocation, initialPositionSet.current]
207
+ [cellRect, updatePopupLocation] // Removed initialPositionSet.current from deps as it's a ref
199
208
  );
200
209
 
201
210
  useLayoutEffect(
202
211
  () => {
203
212
  updatePopupLocation(popupLocation);
204
213
  },
205
- [windowSize, cellRect]
214
+ [windowSize, cellRect, updatePopupLocation, popupLocation]
206
215
  );
207
216
 
208
217
  const validationSchema = useMemo(() => {
@@ -216,9 +225,28 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
216
225
  }, [collection, entityId, propertyKey, customFieldValidator]);
217
226
 
218
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.
219
230
  return updatePopupLocation(popupLocation);
220
231
  }, [popupLocation, updatePopupLocation]);
221
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
+
222
250
  const saveValue = async (values: M) => {
223
251
  setSavingError(null);
224
252
  if (inputCollection && entity && onCellValueChange && propertyKey) {
@@ -273,6 +301,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
273
301
  formex,
274
302
  status: "existing",
275
303
  openEntityMode: "side_panel",
304
+ disabled: false,
276
305
  };
277
306
 
278
307
  const property: ResolvedProperty<any> | undefined = propertyKey && getPropertyInPath(collection?.properties ?? {} as ResolvedProperties, propertyKey as string);
@@ -285,7 +314,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
285
314
  underlyingValueHasChanged: false,
286
315
  context: formContext,
287
316
  partOfArray: false,
288
- minimalistView: false,
317
+ minimalistView: true,
289
318
  autoFocus: open
290
319
  }
291
320
  : undefined;
@@ -369,7 +398,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
369
398
  } cursor-grab overflow-visible`}
370
399
  ref={draggableRef}>
371
400
 
372
- <ElementResizeListener onResize={adaptResize}/>
401
+ {/* ElementResizeListener removed from here */}
373
402
 
374
403
  <div
375
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
  }
@@ -93,6 +93,10 @@ export type EntityCollectionViewProps<M extends Record<string, any>> = {
93
93
  * It defaults to the collection path if not provided.
94
94
  */
95
95
  fullPath?: string;
96
+ /**
97
+ * Full path using navigation ids.
98
+ */
99
+ fullIdPath?: string;
96
100
  /**
97
101
  * If this is a subcollection, specify the parent collection ids.
98
102
  */
@@ -138,6 +142,7 @@ export type EntityCollectionViewProps<M extends Record<string, any>> = {
138
142
  export const EntityCollectionView = React.memo(
139
143
  function EntityCollectionView<M extends Record<string, any>>({
140
144
  fullPath: fullPathProp,
145
+ fullIdPath,
141
146
  parentCollectionIds,
142
147
  isSubCollection,
143
148
  className,
@@ -146,10 +151,11 @@ export const EntityCollectionView = React.memo(
146
151
  }: EntityCollectionViewProps<M>
147
152
  ) {
148
153
 
154
+
149
155
  const context = useFireCMSContext();
156
+ const navigation = useNavigationController();
150
157
  const fullPath = fullPathProp ?? collectionProp.path;
151
158
  const dataSource = useDataSource(collectionProp);
152
- const navigation = useNavigationController();
153
159
  const sideEntityController = useSideEntityController();
154
160
  const authController = useAuthController();
155
161
  const userConfigPersistence = useUserConfigurationPersistence();
@@ -240,6 +246,7 @@ export const EntityCollectionView = React.memo(
240
246
  navigateToEntity({
241
247
  navigation,
242
248
  path,
249
+ fullIdPath,
243
250
  sideEntityController,
244
251
  openEntityMode,
245
252
  collection,
@@ -259,6 +266,7 @@ export const EntityCollectionView = React.memo(
259
266
  collection,
260
267
  entityId: undefined,
261
268
  path: fullPath,
269
+ fullIdPath,
262
270
  sideEntityController,
263
271
  navigation,
264
272
  onClose: unselectNavigatedEntity
@@ -378,7 +386,8 @@ export const EntityCollectionView = React.memo(
378
386
  const resolvedCollection = useMemo(() => resolveCollection<M>({
379
387
  collection,
380
388
  path: fullPath,
381
- propertyConfigs: customizationController.propertyConfigs
389
+ propertyConfigs: customizationController.propertyConfigs,
390
+ authController,
382
391
  }), [collection, fullPath]);
383
392
 
384
393
  const getPropertyFor = useCallback(({
@@ -399,7 +408,8 @@ export const EntityCollectionView = React.memo(
399
408
  path: entity.path,
400
409
  values: entity.values,
401
410
  entityId: entity.id,
402
- propertyConfigs: customizationController.propertyConfigs
411
+ propertyConfigs: customizationController.propertyConfigs,
412
+ authController
403
413
  });
404
414
  }, [collection.properties, customizationController.propertyConfigs, resolvedCollection.properties]);
405
415
 
@@ -415,6 +425,7 @@ export const EntityCollectionView = React.memo(
415
425
  Builder: ({ entity }) => (
416
426
  <Button color={"primary"}
417
427
  variant={"outlined"}
428
+ className={"max-w-full truncate justify-start"}
418
429
  startIcon={<KeyboardTabIcon size={"small"}/>}
419
430
  onClick={(event: any) => {
420
431
  event.stopPropagation();
@@ -424,6 +435,7 @@ export const EntityCollectionView = React.memo(
424
435
  entityId: entity.id,
425
436
  selectedTab: subcollection.id ?? subcollection.path,
426
437
  path: fullPath,
438
+ fullIdPath,
427
439
  navigation,
428
440
  sideEntityController
429
441
  })
@@ -527,7 +539,7 @@ export const EntityCollectionView = React.memo(
527
539
  highlightEntity={setHighlightedEntity}
528
540
  unhighlightEntity={unselectNavigatedEntity}
529
541
  collection={collection}
530
- fullPath={fullPath}
542
+ fullPath={fullIdPath ?? fullPath}
531
543
  actions={actions}
532
544
  hideId={collection?.hideIdFromCollection}
533
545
  onCollectionChange={updateLastDeleteTimestamp}
@@ -691,6 +703,7 @@ export const EntityCollectionView = React.memo(
691
703
  getIdColumnWidth={getIdColumnWidth}
692
704
  additionalIDHeaderWidget={<EntityIdHeaderWidget
693
705
  path={fullPath}
706
+ fullIdPath={fullIdPath ?? fullPath}
694
707
  collection={collection}/>}
695
708
  openEntityMode={openEntityMode}
696
709
  />
@@ -737,10 +750,13 @@ export const EntityCollectionView = React.memo(
737
750
  equal(a.defaultSize, b.defaultSize) &&
738
751
  equal(a.initialFilter, b.initialFilter) &&
739
752
  equal(a.initialSort, b.initialSort) &&
753
+ equal(a.includeJsonView, b.includeJsonView) &&
740
754
  equal(a.textSearchEnabled, b.textSearchEnabled) &&
741
755
  equal(a.additionalFields, b.additionalFields) &&
742
756
  equal(a.sideDialogWidth, b.sideDialogWidth) &&
743
757
  equal(a.openEntityMode, b.openEntityMode) &&
758
+ equal(a.exportable, b.exportable) &&
759
+ equal(a.history, b.history) &&
744
760
  equal(a.forceFilter, b.forceFilter);
745
761
  }) as React.FunctionComponent<EntityCollectionViewProps<any>>
746
762
 
@@ -807,10 +823,12 @@ function buildPropertyWidthOverwrite(key: string, width: number): PartialEntityC
807
823
 
808
824
  function EntityIdHeaderWidget({
809
825
  collection,
810
- path
826
+ path,
827
+ fullIdPath
811
828
  }: {
812
829
  collection: EntityCollection,
813
- path: string
830
+ path: string,
831
+ fullIdPath: string
814
832
  }) {
815
833
 
816
834
  const navigation = useNavigationController();
@@ -848,6 +866,7 @@ function EntityIdHeaderWidget({
848
866
  collection,
849
867
  entityId,
850
868
  path,
869
+ fullIdPath,
851
870
  sideEntityController,
852
871
  navigation
853
872
  })
@@ -882,6 +901,7 @@ function EntityIdHeaderWidget({
882
901
  collection,
883
902
  entityId: id,
884
903
  path,
904
+ fullIdPath,
885
905
  sideEntityController,
886
906
  navigation
887
907
  })
@@ -0,0 +1,66 @@
1
+ import React, { useRef } from "react";
2
+ import { Highlight, themes } from "prism-react-renderer";
3
+ import { useModeController } from "../hooks";
4
+
5
+ export function EntityJsonPreview({ values }: { values: object }) {
6
+ const code = JSON.stringify(values, null, "\t");
7
+ const { mode } = useModeController();
8
+ const preRef = useRef<HTMLPreElement>(null);
9
+
10
+ // Global keydown handler
11
+ // const handleGlobalKeyDown = useCallback((e: KeyboardEvent) => {
12
+ // // Check for Control (Windows/Linux) or Command (macOS) + "a":
13
+ // if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a") {
14
+ // // If our code view is mounted, perform selection
15
+ // if (preRef.current) {
16
+ // e.preventDefault();
17
+ // e.stopPropagation();
18
+ //
19
+ // const selection = window.getSelection();
20
+ // const range = document.createRange();
21
+ // range.selectNodeContents(preRef.current);
22
+ // if (selection) {
23
+ // selection.removeAllRanges();
24
+ // selection.addRange(range);
25
+ // }
26
+ // }
27
+ // }
28
+ // }, []);
29
+
30
+ // // Attach the global keydown listener when component mounts,
31
+ // // and remove it when it unmounts.
32
+ // useEffect(() => {
33
+ // document.addEventListener("keydown", handleGlobalKeyDown);
34
+ // return () => {
35
+ // document.removeEventListener("keydown", handleGlobalKeyDown);
36
+ // };
37
+ // }, [handleGlobalKeyDown]);
38
+
39
+ return (
40
+ <Highlight
41
+ theme={mode === "dark" ? themes.vsDark : themes.github}
42
+ code={code}
43
+ language="json"
44
+ >
45
+ {({ style, tokens, getLineProps, getTokenProps }) => (
46
+ <pre
47
+ // Bind the ref to our pre element so we can select its contents.
48
+ ref={preRef}
49
+ style={{
50
+ ...style,
51
+ backgroundColor: "inherit"
52
+ }}
53
+ className="max-w-6xl mx-auto p-8 rounded text-sm"
54
+ >
55
+ {tokens.map((line, i) => (
56
+ <div key={i} {...getLineProps({ line })} className="text-wrap">
57
+ {line.map((token, key) => (
58
+ <span key={key} {...getTokenProps({ token })} className="word-break" />
59
+ ))}
60
+ </div>
61
+ ))}
62
+ </pre>
63
+ )}
64
+ </Highlight>
65
+ );
66
+ }