@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.
- package/dist/components/ArrayContainer.d.ts +7 -12
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
- package/dist/components/EntityPreview.d.ts +4 -2
- package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
- package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
- package/dist/components/HomePage/NavigationCard.d.ts +3 -1
- package/dist/components/HomePage/NavigationCardBinding.d.ts +3 -2
- package/dist/components/HomePage/NavigationGroup.d.ts +7 -1
- package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
- package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
- package/dist/components/index.d.ts +1 -0
- package/dist/core/EntityEditView.d.ts +3 -0
- package/dist/core/EntityEditViewFormActions.d.ts +1 -1
- package/dist/core/field_configs.d.ts +1 -1
- package/dist/form/EntityForm.d.ts +2 -1
- package/dist/form/EntityFormActions.d.ts +6 -2
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
- package/dist/form/index.d.ts +1 -0
- package/dist/hooks/useBuildNavigationController.d.ts +50 -8
- package/dist/index.es.js +3259 -1649
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +3265 -1658
- package/dist/index.umd.js.map +1 -1
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/collections.d.ts +28 -4
- package/dist/types/customization_controller.d.ts +8 -0
- package/dist/types/entity_actions.d.ts +46 -6
- package/dist/types/firecms.d.ts +10 -1
- package/dist/types/navigation.d.ts +26 -5
- package/dist/types/plugins.d.ts +32 -1
- package/dist/types/properties.d.ts +7 -0
- package/dist/types/property_config.d.ts +1 -1
- package/dist/types/side_entity_controller.d.ts +8 -0
- package/dist/util/callbacks.d.ts +2 -0
- package/dist/util/icons.d.ts +2 -2
- package/dist/util/index.d.ts +1 -0
- package/dist/util/navigation_from_path.d.ts +4 -0
- package/dist/util/resolutions.d.ts +2 -1
- package/package.json +8 -6
- package/src/components/ArrayContainer.tsx +409 -294
- package/src/components/ClearFilterSortButton.tsx +1 -1
- package/src/components/ConfirmationDialog.tsx +10 -9
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +6 -0
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +26 -4
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +53 -27
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +11 -4
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +3 -2
- package/src/components/EntityPreview.tsx +18 -14
- package/src/components/ErrorView.tsx +1 -1
- package/src/components/FireCMSLogo.tsx +7 -51
- package/src/components/HomePage/DefaultHomePage.tsx +491 -156
- package/src/components/HomePage/FavouritesView.tsx +3 -3
- package/src/components/HomePage/HomePageDnD.tsx +599 -0
- package/src/components/HomePage/NavigationCard.tsx +47 -38
- package/src/components/HomePage/NavigationCardBinding.tsx +13 -7
- package/src/components/HomePage/NavigationGroup.tsx +63 -29
- package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
- package/src/components/SelectableTable/SelectableTable.tsx +140 -143
- package/src/components/UnsavedChangesDialog.tsx +6 -2
- package/src/components/VirtualTable/VirtualTable.tsx +8 -30
- package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
- package/src/components/common/default_entity_actions.tsx +25 -9
- package/src/components/common/useDataSourceTableController.tsx +2 -2
- package/src/components/index.tsx +2 -0
- package/src/core/DefaultDrawer.tsx +8 -8
- package/src/core/DrawerNavigationItem.tsx +1 -1
- package/src/core/EntityEditView.tsx +65 -18
- package/src/core/EntityEditViewFormActions.tsx +154 -29
- package/src/core/EntitySidePanel.tsx +19 -21
- package/src/core/FireCMS.tsx +9 -1
- package/src/core/field_configs.tsx +15 -1
- package/src/form/EntityForm.tsx +37 -5
- package/src/form/EntityFormActions.tsx +51 -9
- package/src/form/PropertyFieldBinding.tsx +0 -1
- package/src/form/components/CustomIdField.tsx +3 -1
- package/src/form/components/LabelWithIcon.tsx +1 -1
- package/src/form/components/StorageItemPreview.tsx +1 -1
- package/src/form/components/StorageUploadProgress.tsx +3 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -2
- package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -2
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +1 -1
- package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
- package/src/form/field_bindings/SelectFieldBinding.tsx +2 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +216 -153
- package/src/form/index.tsx +1 -0
- package/src/hooks/data/save.ts +0 -6
- package/src/hooks/useBuildNavigationController.tsx +299 -96
- package/src/internal/useBuildSideEntityController.tsx +8 -5
- package/src/preview/PropertyPreview.tsx +14 -0
- package/src/preview/components/ImagePreview.tsx +2 -2
- package/src/preview/components/ReferencePreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
- package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
- package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
- package/src/routes/FireCMSRoute.tsx +3 -3
- package/src/types/analytics.ts +3 -0
- package/src/types/collections.ts +32 -4
- package/src/types/customization_controller.tsx +9 -0
- package/src/types/entity_actions.tsx +57 -6
- package/src/types/firecms.tsx +11 -2
- package/src/types/navigation.ts +34 -6
- package/src/types/plugins.tsx +40 -1
- package/src/types/properties.ts +8 -0
- package/src/types/property_config.tsx +1 -0
- package/src/types/side_entity_controller.tsx +10 -5
- package/src/util/callbacks.ts +119 -0
- package/src/util/icons.tsx +22 -7
- package/src/util/index.ts +1 -0
- package/src/util/join_collections.ts +3 -1
- package/src/util/navigation_from_path.ts +15 -5
- package/src/util/navigation_utils.ts +72 -57
- package/src/util/objects.ts +53 -20
- package/src/util/resolutions.ts +13 -1
- package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
- 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
|
|
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
|
|
3
|
+
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, LoadingButton } from "@firecms/ui";
|
|
4
4
|
|
|
5
5
|
export function ConfirmationDialog({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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 (
|
|
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.
|
|
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
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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,
|
|
161
|
-
y: Math.max(0, Math.min(y,
|
|
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
|
-
}, [
|
|
161
|
+
}, []);
|
|
164
162
|
|
|
165
|
-
const updatePopupLocation = useCallback((
|
|
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)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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 (!
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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={
|
|
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
|
-
|
|
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
|
-
|
|
49
|
+
previewKeys,
|
|
47
50
|
onClick,
|
|
48
51
|
size,
|
|
49
52
|
includeId = true,
|
|
53
|
+
includeTitle = true,
|
|
50
54
|
includeEntityLink = true,
|
|
51
|
-
|
|
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,
|
|
76
|
-
[
|
|
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
|
|
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
|
|
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={
|
|
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={"
|
|
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={"
|
|
184
|
+
<KeyboardTabIcon size={"small"}/>
|
|
181
185
|
</IconButton>
|
|
182
186
|
</Tooltip>}
|
|
183
187
|
|