@firecms/core 3.0.0-alpha.80 → 3.0.0-alpha.82
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/EntityCollectionTable/EntityCollectionTableProps.d.ts +3 -3
- package/dist/components/EntityCollectionTable/column_utils.d.ts +2 -2
- package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +5 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +1 -1
- package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/contexts/AnalyticsContext.d.ts +3 -0
- package/dist/contexts/CustomizationControllerContext.d.ts +3 -0
- package/dist/core/field_configs.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useAnalyticsController.d.ts +5 -0
- package/dist/hooks/useCustomizationController.d.ts +11 -0
- package/dist/hooks/useFireCMSContext.d.ts +0 -1
- package/dist/index.es.js +4614 -4592
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildCustomizationController.d.ts +2 -0
- package/dist/types/analytics_controller.d.ts +7 -0
- package/dist/types/collections.d.ts +3 -0
- package/dist/types/customization_controller.d.ts +47 -0
- package/dist/types/entity_callbacks.d.ts +17 -3
- package/dist/types/firecms_context.d.ts +6 -46
- package/dist/types/index.d.ts +2 -0
- package/dist/util/icons.d.ts +4 -1
- package/dist/util/useTraceUpdate.d.ts +1 -1
- package/package.json +3 -3
- package/src/components/DeleteEntityDialog.tsx +9 -3
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +28 -5
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +4 -5
- package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +125 -122
- package/src/components/EntityCollectionTable/filters/DateTimeFilterField.tsx +2 -2
- package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +4 -3
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +74 -50
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -3
- package/src/components/EntityPreview.tsx +11 -7
- package/src/components/HomePage/DefaultHomePage.tsx +9 -8
- package/src/components/HomePage/NavigationCollectionCard.tsx +6 -5
- package/src/components/ReferenceSelectionInner.tsx +13 -9
- package/src/components/VirtualTable/VirtualTable.tsx +23 -20
- package/src/components/VirtualTable/VirtualTableCell.tsx +27 -9
- package/src/components/VirtualTable/VirtualTableHeader.tsx +1 -1
- package/src/components/VirtualTable/VirtualTableProps.tsx +2 -2
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +2 -2
- package/src/contexts/AnalyticsContext.tsx +4 -0
- package/src/contexts/CustomizationControllerContext.tsx +4 -0
- package/src/core/Drawer.tsx +7 -6
- package/src/core/FireCMS.tsx +39 -29
- package/src/form/EntityForm.tsx +16 -5
- package/src/form/PropertyFieldBinding.tsx +8 -10
- package/src/form/components/CustomIdField.tsx +7 -5
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +1 -2
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +2 -2
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +4 -6
- package/src/hooks/data/save.ts +8 -3
- package/src/hooks/index.tsx +1 -0
- package/src/hooks/useAnalyticsController.tsx +8 -0
- package/src/hooks/useCustomizationController.tsx +14 -0
- package/src/hooks/useFireCMSContext.tsx +8 -7
- package/src/internal/EntityView.tsx +4 -2
- package/src/internal/useBuildCustomizationController.tsx +5 -0
- package/src/internal/useBuildSideDialogsController.tsx +0 -1
- package/src/preview/PropertyPreview.tsx +4 -3
- package/src/preview/components/DatePreview.tsx +4 -5
- package/src/preview/components/ReferencePreview.tsx +17 -8
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +3 -3
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +3 -3
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +3 -3
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -4
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +3 -3
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +3 -3
- package/src/types/analytics_controller.tsx +10 -0
- package/src/types/collections.ts +3 -0
- package/src/types/customization_controller.tsx +58 -0
- package/src/types/entity_callbacks.ts +22 -3
- package/src/types/firecms_context.tsx +6 -51
- package/src/types/index.ts +2 -0
- package/src/util/icons.tsx +22 -19
- package/src/util/useTraceUpdate.tsx +5 -5
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React, { useCallback, useState } from "react";
|
|
2
|
+
import equal from "react-fast-compare"
|
|
3
|
+
|
|
2
4
|
import { ReferencePreview, ReferencePreviewContainer } from "../../../preview";
|
|
3
5
|
import { CollectionSize, Entity, EntityCollection, EntityReference, FilterValues } from "../../../types";
|
|
4
6
|
|
|
5
7
|
import { getPreviewSizeFrom } from "../../../preview/util";
|
|
6
8
|
import { getReferenceFrom } from "../../../util";
|
|
7
|
-
import {
|
|
9
|
+
import { useCustomizationController, useNavigationController, useReferenceDialog } from "../../../hooks";
|
|
8
10
|
import { ErrorView } from "../../ErrorView";
|
|
9
11
|
import { Button } from "@firecms/ui";
|
|
10
12
|
|
|
@@ -22,14 +24,14 @@ type TableReferenceFieldProps = {
|
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
export function TableReferenceField(props: TableReferenceFieldProps) {
|
|
27
|
+
const customizationController = useCustomizationController();
|
|
25
28
|
|
|
26
|
-
const context = useFireCMSContext();
|
|
27
29
|
const navigationController = useNavigationController();
|
|
28
30
|
const { path } = props;
|
|
29
31
|
const collection = navigationController.getCollection<EntityCollection>(path);
|
|
30
32
|
if (!collection) {
|
|
31
|
-
if (
|
|
32
|
-
return <
|
|
33
|
+
if (customizationController.components?.missingReference) {
|
|
34
|
+
return <customizationController.components.missingReference path={path}/>;
|
|
33
35
|
} else {
|
|
34
36
|
throw Error(`Couldn't find the corresponding collection view for the path: ${path}`);
|
|
35
37
|
}
|
|
@@ -37,123 +39,124 @@ export function TableReferenceField(props: TableReferenceFieldProps) {
|
|
|
37
39
|
return <TableReferenceFieldSuccess {...props} collection={collection}/>;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
path,
|
|
49
|
-
size,
|
|
50
|
-
previewProperties,
|
|
51
|
-
title,
|
|
52
|
-
disabled,
|
|
53
|
-
forceFilter,
|
|
54
|
-
collection
|
|
55
|
-
} = props;
|
|
56
|
-
|
|
57
|
-
const [onHover, setOnHover] = useState(false);
|
|
58
|
-
|
|
59
|
-
const hoverTrue = useCallback(() => setOnHover(true), []);
|
|
60
|
-
const hoverFalse = useCallback(() => setOnHover(false), []);
|
|
61
|
-
|
|
62
|
-
const onSingleEntitySelected = useCallback((entity: Entity<any>) => {
|
|
63
|
-
updateValue(entity ? getReferenceFrom(entity) : null);
|
|
64
|
-
}, [updateValue]);
|
|
65
|
-
|
|
66
|
-
const onMultipleEntitiesSelected = useCallback((entities: Entity<any>[]) => {
|
|
67
|
-
updateValue(entities.map((e) => getReferenceFrom(e)));
|
|
68
|
-
}, [updateValue]);
|
|
69
|
-
|
|
70
|
-
const selectedEntityIds = internalValue
|
|
71
|
-
? (Array.isArray(internalValue)
|
|
72
|
-
? internalValue.map((ref) => ref.id)
|
|
73
|
-
: internalValue.id ? [internalValue.id] : [])
|
|
74
|
-
: [];
|
|
75
|
-
|
|
76
|
-
const referenceDialogController = useReferenceDialog({
|
|
42
|
+
export const TableReferenceFieldSuccess = React.memo(
|
|
43
|
+
function TableReferenceFieldSuccess(props: TableReferenceFieldProps & {
|
|
44
|
+
collection: EntityCollection;
|
|
45
|
+
}) {
|
|
46
|
+
const {
|
|
47
|
+
name,
|
|
48
|
+
internalValue,
|
|
49
|
+
updateValue,
|
|
77
50
|
multiselect,
|
|
78
51
|
path,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
forceFilter
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
52
|
+
size,
|
|
53
|
+
previewProperties,
|
|
54
|
+
title,
|
|
55
|
+
disabled,
|
|
56
|
+
forceFilter,
|
|
57
|
+
collection
|
|
58
|
+
} = props;
|
|
59
|
+
|
|
60
|
+
const [onHover, setOnHover] = useState(false);
|
|
61
|
+
|
|
62
|
+
const hoverTrue = useCallback(() => setOnHover(true), []);
|
|
63
|
+
const hoverFalse = useCallback(() => setOnHover(false), []);
|
|
64
|
+
|
|
65
|
+
const onSingleEntitySelected = useCallback((entity: Entity<any>) => {
|
|
66
|
+
updateValue(entity ? getReferenceFrom(entity) : null);
|
|
67
|
+
}, [updateValue]);
|
|
68
|
+
|
|
69
|
+
const onMultipleEntitiesSelected = useCallback((entities: Entity<any>[]) => {
|
|
70
|
+
updateValue(entities.map((e) => getReferenceFrom(e)));
|
|
71
|
+
}, [updateValue]);
|
|
72
|
+
|
|
73
|
+
const selectedEntityIds = internalValue
|
|
74
|
+
? (Array.isArray(internalValue)
|
|
75
|
+
? internalValue.map((ref) => ref.id)
|
|
76
|
+
: internalValue.id ? [internalValue.id] : [])
|
|
77
|
+
: [];
|
|
78
|
+
|
|
79
|
+
const referenceDialogController = useReferenceDialog({
|
|
80
|
+
multiselect,
|
|
81
|
+
path,
|
|
82
|
+
collection,
|
|
83
|
+
onMultipleEntitiesSelected,
|
|
84
|
+
onSingleEntitySelected,
|
|
85
|
+
selectedEntityIds,
|
|
86
|
+
forceFilter
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const handleOpen = useCallback(() => {
|
|
91
|
+
if (disabled)
|
|
92
|
+
return;
|
|
93
|
+
referenceDialogController.open();
|
|
94
|
+
}, [disabled, referenceDialogController]);
|
|
95
|
+
|
|
96
|
+
const valueNotSet = !internalValue || (Array.isArray(internalValue) && internalValue.length === 0);
|
|
97
|
+
|
|
98
|
+
const buildSingleReferenceField = () => {
|
|
99
|
+
if (internalValue instanceof EntityReference)
|
|
100
|
+
return <ReferencePreview
|
|
101
|
+
onClick={disabled ? undefined : handleOpen}
|
|
102
|
+
size={getPreviewSizeFrom(size)}
|
|
103
|
+
reference={internalValue as EntityReference}
|
|
104
|
+
onHover={onHover}
|
|
105
|
+
disabled={!path}
|
|
106
|
+
previewProperties={previewProperties}
|
|
107
|
+
/>;
|
|
108
|
+
else
|
|
109
|
+
return <ReferencePreviewContainer
|
|
110
|
+
onClick={disabled ? undefined : handleOpen}
|
|
111
|
+
size={getPreviewSizeFrom(size)}>
|
|
112
|
+
<ErrorView title="Value is not a reference." error={"Click to edit"}/>
|
|
113
|
+
</ReferencePreviewContainer>;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const buildMultipleReferenceField = () => {
|
|
117
|
+
if (Array.isArray(internalValue))
|
|
118
|
+
return <>
|
|
119
|
+
{internalValue.map((reference, index) =>
|
|
120
|
+
<div className="m-1 w-full"
|
|
121
|
+
key={`preview_array_ref_${name}_${index}`}>
|
|
122
|
+
<ReferencePreview
|
|
123
|
+
onClick={disabled ? undefined : handleOpen}
|
|
124
|
+
size={"tiny"}
|
|
125
|
+
reference={reference}
|
|
126
|
+
onHover={onHover}
|
|
127
|
+
disabled={!path}
|
|
128
|
+
previewProperties={previewProperties}
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
</>;
|
|
134
|
+
else
|
|
135
|
+
return <ErrorView error={"Data is not an array of references"}/>;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
if (!collection)
|
|
139
|
+
return <ErrorView error={"The specified collection does not exist"}/>;
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div className="w-full"
|
|
143
|
+
onMouseEnter={hoverTrue}
|
|
144
|
+
onMouseMove={hoverTrue}
|
|
145
|
+
onMouseLeave={hoverFalse}>
|
|
146
|
+
|
|
147
|
+
{internalValue && !multiselect && buildSingleReferenceField()}
|
|
148
|
+
|
|
149
|
+
{internalValue && multiselect && buildMultipleReferenceField()}
|
|
150
|
+
|
|
151
|
+
{valueNotSet &&
|
|
152
|
+
<Button
|
|
153
|
+
onClick={handleOpen}
|
|
154
|
+
size={"small"}
|
|
155
|
+
variant="outlined"
|
|
156
|
+
color="primary">
|
|
157
|
+
Edit {title}
|
|
158
|
+
</Button>}
|
|
159
|
+
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
}, equal);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { VirtualTableWhereFilterOp } from "../../VirtualTable";
|
|
3
3
|
import { DateTimeField, Select, SelectItem } from "@firecms/ui";
|
|
4
|
-
import {
|
|
4
|
+
import { useCustomizationController } from "../../../hooks";
|
|
5
5
|
|
|
6
6
|
interface DateTimeFilterFieldProps {
|
|
7
7
|
name: string,
|
|
@@ -36,7 +36,7 @@ export function DateTimeFilterField({
|
|
|
36
36
|
title
|
|
37
37
|
}: DateTimeFilterFieldProps) {
|
|
38
38
|
|
|
39
|
-
const { locale } =
|
|
39
|
+
const { locale } = useCustomizationController();
|
|
40
40
|
const possibleOperations: (keyof typeof operationLabels) [] = isArray
|
|
41
41
|
? ["array-contains"]
|
|
42
42
|
: ["==", "!=", ">", "<", ">=", "<="];
|
|
@@ -14,7 +14,7 @@ export const editEntityAction: EntityAction = {
|
|
|
14
14
|
unhighlightEntity
|
|
15
15
|
}): Promise<void> {
|
|
16
16
|
highlightEntity?.(entity);
|
|
17
|
-
context.onAnalyticsEvent?.("entity_click", {
|
|
17
|
+
context.analyticsController?.onAnalyticsEvent?.("entity_click", {
|
|
18
18
|
path: entity.path,
|
|
19
19
|
entityId: entity.id
|
|
20
20
|
});
|
|
@@ -40,7 +40,7 @@ export const copyEntityAction: EntityAction = {
|
|
|
40
40
|
unhighlightEntity
|
|
41
41
|
}): Promise<void> {
|
|
42
42
|
highlightEntity?.(entity);
|
|
43
|
-
context.onAnalyticsEvent?.("copy_entity_click", {
|
|
43
|
+
context.analyticsController?.onAnalyticsEvent?.("copy_entity_click", {
|
|
44
44
|
path: entity.path,
|
|
45
45
|
entityId: entity.id
|
|
46
46
|
});
|
|
@@ -107,7 +107,7 @@ export const deleteEntityAction: EntityAction = {
|
|
|
107
107
|
callbacks={collection.callbacks}
|
|
108
108
|
open={open}
|
|
109
109
|
onEntityDelete={() => {
|
|
110
|
-
context.onAnalyticsEvent?.("single_entity_deleted", {
|
|
110
|
+
context.analyticsController?.onAnalyticsEvent?.("single_entity_deleted", {
|
|
111
111
|
path: fullPath
|
|
112
112
|
});
|
|
113
113
|
selectionController?.setSelectedEntities(selectionController.selectedEntities.filter(e => e.id !== entity.id));
|
|
@@ -23,7 +23,7 @@ import { ErrorView, OnCellValueChangeParams } from "../../../../components";
|
|
|
23
23
|
import { getPropertyInPath, isReadOnly, resolveCollection } from "../../../../util";
|
|
24
24
|
import { Button, ClearIcon, DialogActions, IconButton, Typography } from "@firecms/ui";
|
|
25
25
|
import { PropertyFieldBinding } from "../../../../form";
|
|
26
|
-
import {
|
|
26
|
+
import { useCustomizationController, useFireCMSContext } from "../../../../hooks";
|
|
27
27
|
|
|
28
28
|
interface PopupFormFieldProps<M extends Record<string, any>> {
|
|
29
29
|
entity?: Entity<M>;
|
|
@@ -65,6 +65,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
|
|
|
65
65
|
|
|
66
66
|
// const dataSource = useDataSource();
|
|
67
67
|
const fireCMSContext = useFireCMSContext();
|
|
68
|
+
const customizationController = useCustomizationController();
|
|
68
69
|
|
|
69
70
|
const [savingError, setSavingError] = React.useState<any>();
|
|
70
71
|
const [popupLocation, setPopupLocation] = useState<{
|
|
@@ -99,7 +100,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
|
|
|
99
100
|
path,
|
|
100
101
|
values: internalValue,
|
|
101
102
|
entityId,
|
|
102
|
-
fields:
|
|
103
|
+
fields: customizationController.propertyConfigs
|
|
103
104
|
})
|
|
104
105
|
: undefined;
|
|
105
106
|
|
|
@@ -315,7 +316,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
|
|
|
315
316
|
</div>
|
|
316
317
|
</>;
|
|
317
318
|
|
|
318
|
-
const plugins =
|
|
319
|
+
const plugins = customizationController.plugins;
|
|
319
320
|
if (plugins) {
|
|
320
321
|
// const formController: FormContext<M> = {
|
|
321
322
|
// values,
|
|
@@ -38,15 +38,17 @@ import { ReferencePreview } from "../../preview";
|
|
|
38
38
|
import {
|
|
39
39
|
saveEntityWithCallbacks,
|
|
40
40
|
useAuthController,
|
|
41
|
-
|
|
42
|
-
useFireCMSContext,
|
|
41
|
+
useCustomizationController,
|
|
42
|
+
useDataSource, useFireCMSContext,
|
|
43
43
|
useLargeLayout,
|
|
44
44
|
useNavigationController,
|
|
45
45
|
useSideEntityController
|
|
46
46
|
} from "../../hooks";
|
|
47
47
|
import { useUserConfigurationPersistence } from "../../hooks/useUserConfigurationPersistence";
|
|
48
48
|
import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
|
|
49
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
useDataSourceEntityCollectionTableController
|
|
51
|
+
} from "../EntityCollectionTable/useDataSourceEntityCollectionTableController";
|
|
50
52
|
import {
|
|
51
53
|
AddIcon,
|
|
52
54
|
Button,
|
|
@@ -72,6 +74,7 @@ import {
|
|
|
72
74
|
editEntityAction
|
|
73
75
|
} from "../EntityCollectionTable/internal/default_entity_actions";
|
|
74
76
|
import { DeleteEntityDialog } from "../DeleteEntityDialog";
|
|
77
|
+
import { useAnalyticsController } from "../../hooks/useAnalyticsController";
|
|
75
78
|
|
|
76
79
|
const COLLECTION_GROUP_PARENT_ID = "collectionGroupParent";
|
|
77
80
|
|
|
@@ -124,6 +127,8 @@ export const EntityCollectionView = React.memo(
|
|
|
124
127
|
const sideEntityController = useSideEntityController();
|
|
125
128
|
const authController = useAuthController();
|
|
126
129
|
const userConfigPersistence = useUserConfigurationPersistence();
|
|
130
|
+
const analyticsController = useAnalyticsController();
|
|
131
|
+
const customizationController = useCustomizationController();
|
|
127
132
|
const context = useFireCMSContext();
|
|
128
133
|
|
|
129
134
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
@@ -133,6 +138,11 @@ export const EntityCollectionView = React.memo(
|
|
|
133
138
|
return (userOverride ? mergeDeep(collectionProp, userOverride) : collectionProp) as EntityCollection<M>;
|
|
134
139
|
}, [collectionProp, fullPath, userConfigPersistence?.getCollectionConfig]);
|
|
135
140
|
|
|
141
|
+
const collectionRef = React.useRef(collection);
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
collectionRef.current = collection;
|
|
144
|
+
}, [collection]);
|
|
145
|
+
|
|
136
146
|
const canCreateEntities = canCreateEntity(collection, authController, fullPathToCollectionSegments(fullPath), null);
|
|
137
147
|
const [selectedNavigationEntity, setSelectedNavigationEntity] = useState<Entity<M> | undefined>(undefined);
|
|
138
148
|
const [deleteEntityClicked, setDeleteEntityClicked] = React.useState<Entity<M> | Entity<M>[] | undefined>(undefined);
|
|
@@ -152,11 +162,12 @@ export const EntityCollectionView = React.memo(
|
|
|
152
162
|
}, [selectedNavigationEntity]);
|
|
153
163
|
|
|
154
164
|
const checkInlineEditing = useCallback((entity?: Entity<any>): boolean => {
|
|
165
|
+
const collection = collectionRef.current;
|
|
155
166
|
if (!canEditEntity(collection, authController, fullPathToCollectionSegments(fullPath), entity ?? null)) {
|
|
156
167
|
return false;
|
|
157
168
|
}
|
|
158
169
|
return collection.inlineEditing === undefined || collection.inlineEditing;
|
|
159
|
-
}, [
|
|
170
|
+
}, [authController, fullPath]);
|
|
160
171
|
|
|
161
172
|
const selectionEnabled = collection.selectionEnabled === undefined || collection.selectionEnabled;
|
|
162
173
|
const hoverRow = !checkInlineEditing();
|
|
@@ -178,7 +189,7 @@ export const EntityCollectionView = React.memo(
|
|
|
178
189
|
|
|
179
190
|
const tableController = useDataSourceEntityCollectionTableController<M>({
|
|
180
191
|
fullPath,
|
|
181
|
-
collection,
|
|
192
|
+
collection: collectionRef.current,
|
|
182
193
|
entitiesDisplayedFirst: [],
|
|
183
194
|
lastDeleteTimestamp
|
|
184
195
|
});
|
|
@@ -191,8 +202,9 @@ export const EntityCollectionView = React.memo(
|
|
|
191
202
|
}, [tableController.setPopupCell]);
|
|
192
203
|
|
|
193
204
|
const onEntityClick = useCallback((clickedEntity: Entity<M>) => {
|
|
205
|
+
const collection = collectionRef.current;
|
|
194
206
|
setSelectedNavigationEntity(clickedEntity);
|
|
195
|
-
|
|
207
|
+
analyticsController.onAnalyticsEvent?.("edit_entity_clicked", {
|
|
196
208
|
path: clickedEntity.path,
|
|
197
209
|
entityId: clickedEntity.id
|
|
198
210
|
});
|
|
@@ -203,10 +215,12 @@ export const EntityCollectionView = React.memo(
|
|
|
203
215
|
updateUrl: true,
|
|
204
216
|
onClose: unselectNavigatedEntity
|
|
205
217
|
});
|
|
206
|
-
}, [
|
|
218
|
+
}, [unselectNavigatedEntity]);
|
|
207
219
|
|
|
208
220
|
const onNewClick = useCallback(() => {
|
|
209
|
-
|
|
221
|
+
|
|
222
|
+
const collection = collectionRef.current;
|
|
223
|
+
analyticsController.onAnalyticsEvent?.("new_entity_click", {
|
|
210
224
|
path: fullPath
|
|
211
225
|
});
|
|
212
226
|
sideEntityController.open({
|
|
@@ -215,38 +229,31 @@ export const EntityCollectionView = React.memo(
|
|
|
215
229
|
updateUrl: true,
|
|
216
230
|
onClose: unselectNavigatedEntity
|
|
217
231
|
});
|
|
218
|
-
}, [fullPath
|
|
219
|
-
|
|
220
|
-
const onSingleDeleteClick = useCallback((selectedEntity: Entity<any>) => {
|
|
221
|
-
context.onAnalyticsEvent?.("single_delete_dialog_open", {
|
|
222
|
-
path: fullPath
|
|
223
|
-
});
|
|
224
|
-
setDeleteEntityClicked(selectedEntity);
|
|
225
|
-
}, [context, fullPath]);
|
|
232
|
+
}, [fullPath]);
|
|
226
233
|
|
|
227
|
-
const onMultipleDeleteClick =
|
|
228
|
-
|
|
234
|
+
const onMultipleDeleteClick = () => {
|
|
235
|
+
analyticsController.onAnalyticsEvent?.("multiple_delete_dialog_open", {
|
|
229
236
|
path: fullPath
|
|
230
237
|
});
|
|
231
238
|
setDeleteEntityClicked(selectedEntities);
|
|
232
|
-
}
|
|
239
|
+
};
|
|
233
240
|
|
|
234
|
-
const internalOnEntityDelete =
|
|
235
|
-
|
|
241
|
+
const internalOnEntityDelete = (_path: string, entity: Entity<M>) => {
|
|
242
|
+
analyticsController.onAnalyticsEvent?.("single_entity_deleted", {
|
|
236
243
|
path: fullPath
|
|
237
244
|
});
|
|
238
245
|
setSelectedEntities((selectedEntities) => selectedEntities.filter((e) => e.id !== entity.id));
|
|
239
246
|
setLastDeleteTimestamp(Date.now());
|
|
240
|
-
}
|
|
247
|
+
};
|
|
241
248
|
|
|
242
|
-
const internalOnMultipleEntitiesDelete =
|
|
243
|
-
|
|
249
|
+
const internalOnMultipleEntitiesDelete = (_path: string, entities: Entity<M>[]) => {
|
|
250
|
+
analyticsController.onAnalyticsEvent?.("multiple_entities_deleted", {
|
|
244
251
|
path: fullPath
|
|
245
252
|
});
|
|
246
253
|
setSelectedEntities([]);
|
|
247
254
|
setDeleteEntityClicked(undefined);
|
|
248
255
|
setLastDeleteTimestamp(Date.now());
|
|
249
|
-
}
|
|
256
|
+
};
|
|
250
257
|
|
|
251
258
|
let AddColumnComponent: React.ComponentType<{
|
|
252
259
|
fullPath: string,
|
|
@@ -255,8 +262,8 @@ export const EntityCollectionView = React.memo(
|
|
|
255
262
|
}> | undefined
|
|
256
263
|
|
|
257
264
|
// we are only using the first plugin that implements this
|
|
258
|
-
if (
|
|
259
|
-
AddColumnComponent =
|
|
265
|
+
if (customizationController?.plugins) {
|
|
266
|
+
AddColumnComponent = customizationController.plugins.find(plugin => plugin.collectionView?.AddColumnComponent)?.collectionView?.AddColumnComponent;
|
|
260
267
|
}
|
|
261
268
|
|
|
262
269
|
const onCollectionModifiedForUser = useCallback((path: string, partialCollection: PartialEntityCollection<M>) => {
|
|
@@ -271,11 +278,13 @@ export const EntityCollectionView = React.memo(
|
|
|
271
278
|
width,
|
|
272
279
|
key
|
|
273
280
|
}: OnColumnResizeParams) => {
|
|
281
|
+
|
|
282
|
+
const collection = collectionRef.current;
|
|
274
283
|
// Only for property columns
|
|
275
284
|
if (!getPropertyInPath(collection.properties, key)) return;
|
|
276
285
|
const localCollection = buildPropertyWidthOverwrite(key, width);
|
|
277
286
|
onCollectionModifiedForUser(fullPath, localCollection);
|
|
278
|
-
}, [
|
|
287
|
+
}, [onCollectionModifiedForUser, fullPath]);
|
|
279
288
|
|
|
280
289
|
const onSizeChanged = useCallback((size: CollectionSize) => {
|
|
281
290
|
if (userConfigPersistence)
|
|
@@ -334,7 +343,7 @@ export const EntityCollectionView = React.memo(
|
|
|
334
343
|
const resolvedCollection = useMemo(() => resolveCollection<M>({
|
|
335
344
|
collection,
|
|
336
345
|
path: fullPath,
|
|
337
|
-
fields:
|
|
346
|
+
fields: customizationController.propertyConfigs
|
|
338
347
|
}), [collection, fullPath]);
|
|
339
348
|
|
|
340
349
|
const getPropertyFor = useCallback(({
|
|
@@ -357,9 +366,9 @@ export const EntityCollectionView = React.memo(
|
|
|
357
366
|
propertyValue,
|
|
358
367
|
values: entity.values,
|
|
359
368
|
entityId: entity.id,
|
|
360
|
-
fields:
|
|
369
|
+
fields: customizationController.propertyConfigs
|
|
361
370
|
});
|
|
362
|
-
}, [collection.properties,
|
|
371
|
+
}, [collection.properties, customizationController.propertyConfigs, fullPath, resolvedCollection.properties]);
|
|
363
372
|
|
|
364
373
|
const displayedColumnIds = useColumnIds(resolvedCollection, true);
|
|
365
374
|
|
|
@@ -448,7 +457,7 @@ export const EntityCollectionView = React.memo(
|
|
|
448
457
|
const uncollapsedActions = entityActions.filter(a => a.collapsed === false);
|
|
449
458
|
const actionsWidth = uncollapsedActions.length * (largeLayout ? 40 : 30);
|
|
450
459
|
return (largeLayout ? (80 + actionsWidth) : (70 + actionsWidth)) + (collapsedActions.length > 0 ? (largeLayout ? 40 : 30) : 0);
|
|
451
|
-
}, [largeLayout
|
|
460
|
+
}, [largeLayout]);
|
|
452
461
|
|
|
453
462
|
const tableRowActionsBuilder = useCallback(({
|
|
454
463
|
entity,
|
|
@@ -500,8 +509,7 @@ export const EntityCollectionView = React.memo(
|
|
|
500
509
|
setPopOverOpen(true);
|
|
501
510
|
e.stopPropagation();
|
|
502
511
|
}
|
|
503
|
-
: undefined}
|
|
504
|
-
>
|
|
512
|
+
: undefined}>
|
|
505
513
|
{`${collection.name}`}
|
|
506
514
|
</Typography>
|
|
507
515
|
|
|
@@ -522,19 +530,20 @@ export const EntityCollectionView = React.memo(
|
|
|
522
530
|
|
|
523
531
|
</Popover>;
|
|
524
532
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
533
|
+
const buildAdditionalHeaderWidget = useCallback(({
|
|
534
|
+
property,
|
|
535
|
+
propertyKey,
|
|
536
|
+
onHover
|
|
537
|
+
}: {
|
|
530
538
|
property: ResolvedProperty,
|
|
531
539
|
propertyKey: string,
|
|
532
540
|
onHover: boolean
|
|
533
|
-
}) {
|
|
534
|
-
|
|
541
|
+
}) => {
|
|
542
|
+
const collection = collectionRef.current;
|
|
543
|
+
if (!customizationController.plugins)
|
|
535
544
|
return null;
|
|
536
545
|
return <>
|
|
537
|
-
{
|
|
546
|
+
{customizationController.plugins.filter(plugin => plugin.collectionView?.HeaderAction)
|
|
538
547
|
.map((plugin, i) => {
|
|
539
548
|
const HeaderAction = plugin.collectionView!.HeaderAction!;
|
|
540
549
|
return <HeaderAction
|
|
@@ -547,7 +556,7 @@ export const EntityCollectionView = React.memo(
|
|
|
547
556
|
parentCollectionIds={parentCollectionIds ?? []}/>;
|
|
548
557
|
})}
|
|
549
558
|
</>;
|
|
550
|
-
}
|
|
559
|
+
}, [customizationController.plugins, fullPath, parentCollectionIds]);
|
|
551
560
|
|
|
552
561
|
const addColumnComponentInternal = AddColumnComponent
|
|
553
562
|
? function () {
|
|
@@ -562,13 +571,13 @@ export const EntityCollectionView = React.memo(
|
|
|
562
571
|
const [textSearchInitialised, setTextSearchInitialised] = useState<boolean>(false);
|
|
563
572
|
let onTextSearchClick: (() => void) | undefined;
|
|
564
573
|
let textSearchEnabled = Boolean(collection.textSearchEnabled);
|
|
565
|
-
if (
|
|
566
|
-
const addTextSearchClickListener =
|
|
574
|
+
if (customizationController?.plugins) {
|
|
575
|
+
const addTextSearchClickListener = customizationController.plugins?.find(p => Boolean(p.collectionView?.onTextSearchClick));
|
|
567
576
|
|
|
568
577
|
onTextSearchClick = addTextSearchClickListener
|
|
569
578
|
? () => {
|
|
570
579
|
setTextSearchLoading(true);
|
|
571
|
-
Promise.all(
|
|
580
|
+
Promise.all(customizationController.plugins?.map(p => {
|
|
572
581
|
if (p.collectionView?.onTextSearchClick)
|
|
573
582
|
return p.collectionView.onTextSearchClick({ context, path: resolvedFullPath, collection, parentCollectionIds });
|
|
574
583
|
return Promise.resolve(true);
|
|
@@ -580,7 +589,7 @@ export const EntityCollectionView = React.memo(
|
|
|
580
589
|
}
|
|
581
590
|
: undefined;
|
|
582
591
|
|
|
583
|
-
|
|
592
|
+
customizationController.plugins?.forEach(p => {
|
|
584
593
|
if (!textSearchEnabled)
|
|
585
594
|
if (p.collectionView?.showTextSearchBar) {
|
|
586
595
|
textSearchEnabled = p.collectionView.showTextSearchBar({ context, path: resolvedFullPath, collection, parentCollectionIds });
|
|
@@ -675,9 +684,24 @@ export const EntityCollectionView = React.memo(
|
|
|
675
684
|
|
|
676
685
|
</div>
|
|
677
686
|
);
|
|
678
|
-
},
|
|
679
|
-
|
|
680
|
-
|
|
687
|
+
}, (a, b) => {
|
|
688
|
+
return equal(a.fullPath, b.fullPath) &&
|
|
689
|
+
equal(a.parentCollectionIds, b.parentCollectionIds) &&
|
|
690
|
+
equal(a.isSubCollection, b.isSubCollection) &&
|
|
691
|
+
equal(a.className, b.className) &&
|
|
692
|
+
equal(a.properties, b.properties) &&
|
|
693
|
+
equal(a.hideIdFromCollection, b.hideIdFromCollection) &&
|
|
694
|
+
equal(a.inlineEditing, b.inlineEditing) &&
|
|
695
|
+
equal(a.selectionEnabled, b.selectionEnabled) &&
|
|
696
|
+
equal(a.selectionController, b.selectionController) &&
|
|
697
|
+
equal(a.Actions, b.Actions) &&
|
|
698
|
+
equal(a.defaultSize, b.defaultSize) &&
|
|
699
|
+
equal(a.textSearchEnabled, b.textSearchEnabled) &&
|
|
700
|
+
equal(a.additionalFields, b.additionalFields) &&
|
|
701
|
+
equal(a.forceFilter, b.forceFilter);
|
|
702
|
+
}) as React.FunctionComponent<EntityCollectionViewProps<any>>
|
|
703
|
+
|
|
704
|
+
export function useSelectionController<M extends Record<string, any> = any>(): SelectionController<M> {
|
|
681
705
|
|
|
682
706
|
const [selectedEntities, setSelectedEntities] = useState<Entity<M>[]>([]);
|
|
683
707
|
|