@firecms/core 3.0.0-alpha.28 → 3.0.0-alpha.30
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/core/FireCMS.d.ts +2 -2
- package/dist/core/builders.d.ts +2 -2
- package/dist/core/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +4 -2
- package/dist/core/components/EntityCollectionTable/internal/EntityCollectionRowActions.d.ts +15 -11
- package/dist/core/components/EntityCollectionTable/internal/default_entity_actions.d.ts +5 -0
- package/dist/core/components/EntityCollectionTable/types.d.ts +1 -37
- package/dist/core/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/core/contexts/DialogsProvider.d.ts +4 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/internal/useBuildNavigationContext.d.ts +3 -3
- package/dist/core/util/resolutions.d.ts +1 -1
- package/dist/hooks/useDialogsController.d.ts +11 -0
- package/dist/index.es.js +8400 -8230
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +67 -67
- package/dist/index.umd.js.map +1 -1
- package/dist/styles.d.ts +1 -1
- package/dist/types/collections.d.ts +36 -13
- package/dist/types/dialogs_controller.d.ts +30 -0
- package/dist/types/entity_actions.d.ts +33 -0
- package/dist/types/firecms.d.ts +5 -5
- package/dist/types/firecms_context.d.ts +7 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/navigation.d.ts +3 -3
- package/dist/types/plugins.d.ts +11 -10
- package/package.json +3 -3
- package/src/components/Sheet.tsx +1 -0
- package/src/core/FireCMS.tsx +9 -6
- package/src/core/builders.ts +6 -6
- package/src/core/components/ArrayContainer.tsx +1 -1
- package/src/core/components/EntityCollectionTable/EntityCollectionTable.tsx +16 -9
- package/src/core/components/EntityCollectionTable/EntityCollectionTableProps.tsx +5 -2
- package/src/core/components/EntityCollectionTable/internal/EntityCollectionRowActions.tsx +96 -79
- package/src/core/components/EntityCollectionTable/internal/default_entity_actions.tsx +107 -0
- package/src/core/components/EntityCollectionTable/types.tsx +1 -56
- package/src/core/components/EntityCollectionView/EntityCollectionView.tsx +66 -42
- package/src/core/components/ReferenceSelectionInner.tsx +2 -2
- package/src/core/components/VirtualTable/VirtualTable.tsx +4 -6
- package/src/core/components/VirtualTable/VirtualTableProps.tsx +1 -1
- package/src/core/contexts/DialogsProvider.tsx +50 -0
- package/src/core/index.tsx +1 -1
- package/src/core/internal/useBuildNavigationContext.tsx +13 -13
- package/src/core/internal/useBuildSideEntityController.tsx +9 -1
- package/src/form/EntityForm.tsx +63 -6
- package/src/hooks/useDialogsController.tsx +14 -0
- package/src/hooks/useFireCMSContext.tsx +5 -11
- package/src/styles.ts +1 -1
- package/src/types/collections.ts +36 -17
- package/src/types/dialogs_controller.tsx +31 -0
- package/src/types/entity_actions.tsx +36 -0
- package/src/types/firecms.tsx +5 -5
- package/src/types/firecms_context.tsx +8 -0
- package/src/types/index.ts +1 -0
- package/src/types/navigation.ts +3 -3
- package/src/types/plugins.tsx +11 -10
- /package/dist/core/contexts/{SnackbarContext.d.ts → SnackbarProvider.d.ts} +0 -0
- /package/src/core/contexts/{SnackbarContext.tsx → SnackbarProvider.tsx} +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import React, { MouseEvent, useCallback } from "react";
|
|
2
|
+
import equal from "react-fast-compare"
|
|
2
3
|
|
|
3
|
-
import { CollectionSize, Entity } from "../../../../types";
|
|
4
|
-
import { Checkbox, IconButton, Tooltip, Typography } from "../../../../components";
|
|
5
|
-
import { useLargeLayout } from "../../../../hooks
|
|
6
|
-
import {
|
|
7
|
-
import { Menu, MenuItem } from "../../../../components/Menu";
|
|
4
|
+
import { CollectionSize, Entity, EntityAction, EntityCollection, SelectionController } from "../../../../types";
|
|
5
|
+
import { Checkbox, cn, IconButton, Menu, MenuItem, Tooltip, Typography } from "../../../../components";
|
|
6
|
+
import { useFireCMSContext, useLargeLayout } from "../../../../hooks";
|
|
7
|
+
import { MoreVertIcon } from "../../../../icons";
|
|
8
8
|
import { Skeleton } from "../../../../components/Skeleton";
|
|
9
|
-
import { cn } from "../../../../components/util/cn";
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
*
|
|
@@ -25,62 +24,57 @@ import { cn } from "../../../../components/util/cn";
|
|
|
25
24
|
*
|
|
26
25
|
* @category Collection components
|
|
27
26
|
*/
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
27
|
+
export const EntityCollectionRowActions = React.memo(function EntityCollectionRowActions({
|
|
28
|
+
entity,
|
|
29
|
+
collection,
|
|
30
|
+
fullPath,
|
|
31
|
+
width,
|
|
32
|
+
frozen,
|
|
33
|
+
isSelected,
|
|
34
|
+
selectionEnabled,
|
|
35
|
+
size,
|
|
36
|
+
highlightEntity,
|
|
37
|
+
onCollectionChange,
|
|
38
|
+
unhighlightEntity,
|
|
39
|
+
actions = [],
|
|
40
|
+
hideId,
|
|
41
|
+
selectionController
|
|
42
|
+
}:
|
|
43
|
+
{
|
|
44
|
+
entity: Entity<any>,
|
|
45
|
+
collection?: EntityCollection<any>,
|
|
46
|
+
fullPath?: string,
|
|
47
|
+
width: number,
|
|
48
|
+
frozen?: boolean,
|
|
49
|
+
size: CollectionSize,
|
|
50
|
+
isSelected: boolean,
|
|
51
|
+
selectionEnabled?: boolean,
|
|
52
|
+
actions?: EntityAction[],
|
|
53
|
+
hideId?: boolean,
|
|
54
|
+
onCollectionChange?: () => void,
|
|
55
|
+
selectionController?: SelectionController;
|
|
56
|
+
highlightEntity?: (entity: Entity<any>) => void;
|
|
57
|
+
unhighlightEntity?: (entity: Entity<any>) => void;
|
|
58
|
+
}) {
|
|
54
59
|
|
|
55
60
|
const largeLayout = useLargeLayout();
|
|
56
61
|
|
|
57
|
-
const
|
|
58
|
-
const copyEnabled = Boolean(onCopyClicked);
|
|
59
|
-
const deleteEnabled = Boolean(onDeleteClicked);
|
|
62
|
+
const context = useFireCMSContext();
|
|
60
63
|
|
|
61
64
|
const onCheckedChange = useCallback((checked: boolean) => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}, [entity, toggleEntitySelection]);
|
|
65
|
-
|
|
66
|
-
const onDeleteClick = useCallback((event: MouseEvent) => {
|
|
67
|
-
event.stopPropagation();
|
|
68
|
-
if (onDeleteClicked)
|
|
69
|
-
onDeleteClicked(entity);
|
|
70
|
-
}, [entity, onDeleteClicked]);
|
|
71
|
-
|
|
72
|
-
const onCopyClick = useCallback((event: MouseEvent) => {
|
|
73
|
-
event.stopPropagation();
|
|
74
|
-
if (onCopyClicked)
|
|
75
|
-
onCopyClicked(entity);
|
|
76
|
-
}, [entity, onCopyClicked]);
|
|
65
|
+
selectionController?.toggleEntitySelection(entity);
|
|
66
|
+
}, [entity, selectionController?.toggleEntitySelection]);
|
|
77
67
|
|
|
78
68
|
const onClick = useCallback((event: MouseEvent) => {
|
|
79
69
|
event.stopPropagation();
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
70
|
+
selectionController?.toggleEntitySelection(entity);
|
|
71
|
+
}, [entity, selectionController?.toggleEntitySelection]);
|
|
72
|
+
|
|
73
|
+
const hasActions = actions.length > 0;
|
|
74
|
+
const hasCollapsedActions = actions.some(a => a.collapsed || a.collapsed === undefined);
|
|
83
75
|
|
|
76
|
+
const collapsedActions = actions.filter(a => a.collapsed || a.collapsed === undefined);
|
|
77
|
+
const uncollapsedActions = actions.filter(a => a.collapsed === false);
|
|
84
78
|
return (
|
|
85
79
|
<div
|
|
86
80
|
onClick={onClick}
|
|
@@ -95,20 +89,58 @@ export function EntityCollectionRowActions<M extends Record<string, any>>({
|
|
|
95
89
|
contain: "strict"
|
|
96
90
|
}}>
|
|
97
91
|
|
|
98
|
-
{(
|
|
92
|
+
{(hasActions || selectionEnabled) &&
|
|
99
93
|
<div className="w-34 flex justify-center">
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
|
|
95
|
+
{uncollapsedActions.map((action, index) => (
|
|
96
|
+
<Tooltip key={index} title={action.name}>
|
|
102
97
|
<IconButton
|
|
103
98
|
onClick={(event: MouseEvent) => {
|
|
104
99
|
event.stopPropagation();
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
action.onClick({
|
|
101
|
+
entity,
|
|
102
|
+
fullPath,
|
|
103
|
+
collection,
|
|
104
|
+
context,
|
|
105
|
+
selectionController,
|
|
106
|
+
highlightEntity,
|
|
107
|
+
unhighlightEntity,
|
|
108
|
+
onCollectionChange
|
|
109
|
+
});
|
|
107
110
|
}}
|
|
108
111
|
size={largeLayout ? "medium" : "small"}>
|
|
109
|
-
|
|
112
|
+
{action.icon}
|
|
110
113
|
</IconButton>
|
|
111
114
|
</Tooltip>
|
|
115
|
+
))}
|
|
116
|
+
|
|
117
|
+
{hasCollapsedActions &&
|
|
118
|
+
<Menu
|
|
119
|
+
trigger={<IconButton
|
|
120
|
+
size={largeLayout ? "medium" : "small"}>
|
|
121
|
+
<MoreVertIcon/>
|
|
122
|
+
</IconButton>}>
|
|
123
|
+
{collapsedActions.map((action, index) => (
|
|
124
|
+
<MenuItem
|
|
125
|
+
key={index}
|
|
126
|
+
onClick={(e) => {
|
|
127
|
+
e.stopPropagation();
|
|
128
|
+
action.onClick({
|
|
129
|
+
entity,
|
|
130
|
+
fullPath,
|
|
131
|
+
collection,
|
|
132
|
+
context,
|
|
133
|
+
selectionController,
|
|
134
|
+
highlightEntity,
|
|
135
|
+
unhighlightEntity,
|
|
136
|
+
onCollectionChange
|
|
137
|
+
});
|
|
138
|
+
}}>
|
|
139
|
+
{action.icon}
|
|
140
|
+
{action.name}
|
|
141
|
+
</MenuItem>
|
|
142
|
+
))}
|
|
143
|
+
</Menu>
|
|
112
144
|
}
|
|
113
145
|
|
|
114
146
|
{selectionEnabled &&
|
|
@@ -120,24 +152,6 @@ export function EntityCollectionRowActions<M extends Record<string, any>>({
|
|
|
120
152
|
/>
|
|
121
153
|
</Tooltip>}
|
|
122
154
|
|
|
123
|
-
{(copyEnabled || deleteEnabled) &&
|
|
124
|
-
<Menu
|
|
125
|
-
trigger={<IconButton
|
|
126
|
-
size={largeLayout ? "medium" : "small"}>
|
|
127
|
-
<MoreVertIcon/>
|
|
128
|
-
</IconButton>}>
|
|
129
|
-
{deleteEnabled && <MenuItem onClick={onDeleteClick}>
|
|
130
|
-
<DeleteIcon/>
|
|
131
|
-
Delete
|
|
132
|
-
</MenuItem>}
|
|
133
|
-
|
|
134
|
-
{copyEnabled && <MenuItem onClick={onCopyClick}>
|
|
135
|
-
<FileCopyIcon/>
|
|
136
|
-
Copy
|
|
137
|
-
</MenuItem>}
|
|
138
|
-
</Menu>
|
|
139
|
-
}
|
|
140
|
-
|
|
141
155
|
</div>}
|
|
142
156
|
|
|
143
157
|
{!hideId && size !== "xs" && (
|
|
@@ -145,7 +159,10 @@ export function EntityCollectionRowActions<M extends Record<string, any>>({
|
|
|
145
159
|
|
|
146
160
|
{entity
|
|
147
161
|
? <Typography
|
|
148
|
-
|
|
162
|
+
onClick={(event) => {
|
|
163
|
+
event.stopPropagation();
|
|
164
|
+
}}
|
|
165
|
+
className={"font-mono select-all"}
|
|
149
166
|
variant={"caption"}
|
|
150
167
|
color={"secondary"}> {entity.id} </Typography>
|
|
151
168
|
: <Skeleton/>
|
|
@@ -156,4 +173,4 @@ export function EntityCollectionRowActions<M extends Record<string, any>>({
|
|
|
156
173
|
</div>
|
|
157
174
|
);
|
|
158
175
|
|
|
159
|
-
}
|
|
176
|
+
}, equal);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { EntityAction } from "../../../../types";
|
|
2
|
+
import { DeleteEntityDialog } from "./DeleteEntityDialog";
|
|
3
|
+
import { ArchiveIcon, DeleteIcon, FileCopyIcon, KeyboardTabIcon } from "../../../../icons";
|
|
4
|
+
|
|
5
|
+
export const editEntityAction: EntityAction = {
|
|
6
|
+
icon: <KeyboardTabIcon/>,
|
|
7
|
+
name: "Edit",
|
|
8
|
+
collapsed: false,
|
|
9
|
+
onClick({
|
|
10
|
+
entity,
|
|
11
|
+
collection,
|
|
12
|
+
context,
|
|
13
|
+
highlightEntity,
|
|
14
|
+
unhighlightEntity
|
|
15
|
+
}): Promise<void> {
|
|
16
|
+
highlightEntity?.(entity);
|
|
17
|
+
context.onAnalyticsEvent?.("entity_click", {
|
|
18
|
+
path: entity.path,
|
|
19
|
+
entityId: entity.id
|
|
20
|
+
});
|
|
21
|
+
context.sideEntityController.open({
|
|
22
|
+
entityId: entity.id,
|
|
23
|
+
path: entity.path,
|
|
24
|
+
collection,
|
|
25
|
+
updateUrl: true,
|
|
26
|
+
onClose: () => unhighlightEntity?.(entity)
|
|
27
|
+
});
|
|
28
|
+
return Promise.resolve(undefined);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const copyEntityAction: EntityAction = {
|
|
33
|
+
icon: <FileCopyIcon/>,
|
|
34
|
+
name: "Copy",
|
|
35
|
+
onClick({
|
|
36
|
+
entity,
|
|
37
|
+
collection,
|
|
38
|
+
context,
|
|
39
|
+
highlightEntity,
|
|
40
|
+
unhighlightEntity
|
|
41
|
+
}): Promise<void> {
|
|
42
|
+
highlightEntity?.(entity);
|
|
43
|
+
context.onAnalyticsEvent?.("copy_entity_click", {
|
|
44
|
+
path: entity.path,
|
|
45
|
+
entityId: entity.id
|
|
46
|
+
});
|
|
47
|
+
context.sideEntityController.open({
|
|
48
|
+
entityId: entity.id,
|
|
49
|
+
path: entity.path,
|
|
50
|
+
copy: true,
|
|
51
|
+
collection,
|
|
52
|
+
updateUrl: true,
|
|
53
|
+
onClose: () => unhighlightEntity?.(entity)
|
|
54
|
+
});
|
|
55
|
+
return Promise.resolve(undefined);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export const archiveEntityAction: EntityAction = {
|
|
59
|
+
icon: <ArchiveIcon/>,
|
|
60
|
+
name: "Archive",
|
|
61
|
+
onClick({
|
|
62
|
+
entity,
|
|
63
|
+
collection,
|
|
64
|
+
context,
|
|
65
|
+
}): Promise<void> {
|
|
66
|
+
// Add your code here
|
|
67
|
+
return Promise.resolve(undefined);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const deleteEntityAction: EntityAction = {
|
|
72
|
+
icon: <DeleteIcon/>,
|
|
73
|
+
name: "Delete",
|
|
74
|
+
onClick({
|
|
75
|
+
entity,
|
|
76
|
+
fullPath,
|
|
77
|
+
collection,
|
|
78
|
+
context,
|
|
79
|
+
selectionController,
|
|
80
|
+
onCollectionChange,
|
|
81
|
+
sideEntityController
|
|
82
|
+
}): Promise<void> {
|
|
83
|
+
const { closeDialog } = context.dialogsController.open({
|
|
84
|
+
key: "delete_entity_dialog_" + entity.id,
|
|
85
|
+
Component: ({ open }) => {
|
|
86
|
+
if (!collection || !fullPath)
|
|
87
|
+
throw new Error("deleteEntityAction: Collection is undefined");
|
|
88
|
+
return <DeleteEntityDialog
|
|
89
|
+
entityOrEntitiesToDelete={entity}
|
|
90
|
+
path={fullPath}
|
|
91
|
+
collection={collection}
|
|
92
|
+
callbacks={collection.callbacks}
|
|
93
|
+
open={open}
|
|
94
|
+
onEntityDelete={() => {
|
|
95
|
+
context.onAnalyticsEvent?.("single_entity_deleted", {
|
|
96
|
+
path: fullPath
|
|
97
|
+
});
|
|
98
|
+
selectionController?.setSelectedEntities(selectionController.selectedEntities.filter(e => e.id !== entity.id));
|
|
99
|
+
onCollectionChange?.();
|
|
100
|
+
sideEntityController?.close();
|
|
101
|
+
}}
|
|
102
|
+
onClose={closeDialog}/>;
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
return Promise.resolve(undefined);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -1,59 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CollectionSize,
|
|
3
|
-
DataSource,
|
|
4
|
-
Entity,
|
|
5
|
-
EntityCollection,
|
|
6
|
-
FireCMSContext,
|
|
7
|
-
ResolvedProperty, SelectedCellProps,
|
|
8
|
-
User
|
|
9
|
-
} from "../../../types";
|
|
10
|
-
|
|
11
|
-
export type EntityCollectionTableProviderProps<M extends Record<string, any>, AdditionalKey extends string, UserType extends User> = {
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Absolute collection path
|
|
15
|
-
*/
|
|
16
|
-
path: string;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Use to resolve the collection properties for specific path, entity id or values
|
|
20
|
-
*/
|
|
21
|
-
collection: EntityCollection<M>
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* List of entities that will be displayed on top, no matter the ordering.
|
|
25
|
-
* This is used for reference fields selection
|
|
26
|
-
*/
|
|
27
|
-
entitiesDisplayedFirst?: Entity<M>[];
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Callback when anywhere on the table is clicked
|
|
31
|
-
*/
|
|
32
|
-
onEntityClick?(entity: Entity<M>): void;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Callback when the selected size of the table is changed
|
|
36
|
-
*/
|
|
37
|
-
onSizeChanged?(size: CollectionSize): void;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Can the table be edited inline
|
|
41
|
-
*/
|
|
42
|
-
inlineEditing: ((entity: Entity<any>) => boolean) | boolean;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Builder for creating the buttons in each row
|
|
46
|
-
* @param entity
|
|
47
|
-
* @param size
|
|
48
|
-
*/
|
|
49
|
-
tableRowActionsBuilder?: ({
|
|
50
|
-
entity,
|
|
51
|
-
size
|
|
52
|
-
}: {
|
|
53
|
-
entity: Entity<M>,
|
|
54
|
-
size: CollectionSize
|
|
55
|
-
}) => React.ReactNode;
|
|
56
|
-
};
|
|
1
|
+
import { CollectionSize, Entity, FireCMSContext, ResolvedProperty, SelectedCellProps } from "../../../types";
|
|
57
2
|
|
|
58
3
|
export type EntityCollectionTableController<M extends Record<string, any>> = {
|
|
59
4
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
AdditionalFieldDelegate,
|
|
7
7
|
CollectionSize,
|
|
8
8
|
Entity,
|
|
9
|
+
EntityAction,
|
|
9
10
|
EntityCollection,
|
|
10
11
|
FilterValues,
|
|
11
12
|
PartialEntityCollection,
|
|
@@ -40,14 +41,14 @@ import {
|
|
|
40
41
|
useAuthController,
|
|
41
42
|
useDataSource,
|
|
42
43
|
useFireCMSContext,
|
|
44
|
+
useLargeLayout,
|
|
43
45
|
useNavigationContext,
|
|
44
46
|
useSideEntityController
|
|
45
47
|
} from "../../../hooks";
|
|
46
48
|
import { useUserConfigurationPersistence } from "../../../hooks/useUserConfigurationPersistence";
|
|
47
49
|
import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
|
|
48
50
|
import { useEntityCollectionTableController } from "../EntityCollectionTable/useEntityCollectionTableController";
|
|
49
|
-
import { Button, cn, IconButton, TextField, Tooltip, Typography } from "../../../components";
|
|
50
|
-
import { Popover } from "../../../components/Popover";
|
|
51
|
+
import { Button, cn, IconButton, Popover, TextField, Tooltip, Typography } from "../../../components";
|
|
51
52
|
import { Skeleton } from "../../../components/Skeleton";
|
|
52
53
|
import { setIn } from "formik";
|
|
53
54
|
import { getSubcollectionColumnId } from "../EntityCollectionTable/internal/common";
|
|
@@ -55,6 +56,11 @@ import { KeyboardTabIcon, SearchIcon } from "../../../icons";
|
|
|
55
56
|
import { useColumnIds } from "./useColumnsIds";
|
|
56
57
|
import { PopupFormField } from "../EntityCollectionTable/internal/popup_field/PopupFormField";
|
|
57
58
|
import { GetPropertyForProps } from "../EntityCollectionTable/EntityCollectionTableProps";
|
|
59
|
+
import {
|
|
60
|
+
copyEntityAction,
|
|
61
|
+
deleteEntityAction,
|
|
62
|
+
editEntityAction
|
|
63
|
+
} from "../EntityCollectionTable/internal/default_entity_actions";
|
|
58
64
|
|
|
59
65
|
const COLLECTION_GROUP_PARENT_ID = "collectionGroupParent";
|
|
60
66
|
|
|
@@ -116,6 +122,7 @@ export const EntityCollectionView = React.memo(
|
|
|
116
122
|
return (userOverride ? mergeDeep(collectionProp, userOverride) : collectionProp) as EntityCollection<M>;
|
|
117
123
|
}, [collectionProp, fullPath, userConfigPersistence?.getCollectionConfig]);
|
|
118
124
|
|
|
125
|
+
const canCreateEntities = canCreateEntity(collection, authController, fullPathToCollectionSegments(fullPath), null);
|
|
119
126
|
const [selectedNavigationEntity, setSelectedNavigationEntity] = useState<Entity<M> | undefined>(undefined);
|
|
120
127
|
const [deleteEntityClicked, setDeleteEntityClicked] = React.useState<Entity<M> | Entity<M>[] | undefined>(undefined);
|
|
121
128
|
|
|
@@ -343,39 +350,8 @@ export const EntityCollectionView = React.memo(
|
|
|
343
350
|
|
|
344
351
|
const displayedColumnIds = useColumnIds(resolvedCollection, true);
|
|
345
352
|
|
|
346
|
-
const onCopyClicked = useCallback((clickedEntity: Entity<M>) => {
|
|
347
|
-
setSelectedNavigationEntity(clickedEntity);
|
|
348
|
-
context.onAnalyticsEvent?.("copy_entity_click", {
|
|
349
|
-
path: clickedEntity.path,
|
|
350
|
-
entityId: clickedEntity.id
|
|
351
|
-
});
|
|
352
|
-
sideEntityController.open({
|
|
353
|
-
entityId: clickedEntity.id,
|
|
354
|
-
path: clickedEntity.path,
|
|
355
|
-
copy: true,
|
|
356
|
-
collection,
|
|
357
|
-
updateUrl: true,
|
|
358
|
-
onClose: unselectNavigatedEntity
|
|
359
|
-
});
|
|
360
|
-
}, [sideEntityController, collection, fullPath, unselectNavigatedEntity]);
|
|
361
|
-
|
|
362
|
-
const onEditClicked = useCallback((clickedEntity: Entity<M>) => {
|
|
363
|
-
setSelectedNavigationEntity(clickedEntity);
|
|
364
|
-
context.onAnalyticsEvent?.("entity_click", {
|
|
365
|
-
path: clickedEntity.path,
|
|
366
|
-
entityId: clickedEntity.id
|
|
367
|
-
});
|
|
368
|
-
sideEntityController.open({
|
|
369
|
-
entityId: clickedEntity.id,
|
|
370
|
-
path: clickedEntity.path,
|
|
371
|
-
collection,
|
|
372
|
-
updateUrl: true,
|
|
373
|
-
onClose: unselectNavigatedEntity
|
|
374
|
-
});
|
|
375
|
-
}, [sideEntityController, collection, fullPath, unselectNavigatedEntity]);
|
|
376
|
-
|
|
377
353
|
const additionalFields = useMemo(() => {
|
|
378
|
-
const subcollectionColumns: AdditionalFieldDelegate<M, any
|
|
354
|
+
const subcollectionColumns: AdditionalFieldDelegate<M, any>[] = collection.subcollections?.map((subcollection) => {
|
|
379
355
|
return {
|
|
380
356
|
id: getSubcollectionColumnId(subcollection),
|
|
381
357
|
name: subcollection.name,
|
|
@@ -401,7 +377,7 @@ export const EntityCollectionView = React.memo(
|
|
|
401
377
|
};
|
|
402
378
|
}) ?? [];
|
|
403
379
|
|
|
404
|
-
const collectionGroupParentCollections: AdditionalFieldDelegate<M, any
|
|
380
|
+
const collectionGroupParentCollections: AdditionalFieldDelegate<M, any>[] = collection.collectionGroup
|
|
405
381
|
? [{
|
|
406
382
|
id: COLLECTION_GROUP_PARENT_ID,
|
|
407
383
|
name: "Parent entities",
|
|
@@ -432,6 +408,32 @@ export const EntityCollectionView = React.memo(
|
|
|
432
408
|
];
|
|
433
409
|
}, [collection, fullPath]);
|
|
434
410
|
|
|
411
|
+
const updateLastDeleteTimestamp = useCallback(() => {
|
|
412
|
+
setLastDeleteTimestamp(Date.now());
|
|
413
|
+
}, []);
|
|
414
|
+
|
|
415
|
+
const largeLayout = useLargeLayout();
|
|
416
|
+
|
|
417
|
+
const getActionsForEntity = useCallback(({ entity, customEntityActions }: { entity?: Entity<M>, customEntityActions?: EntityAction[] }): EntityAction[] => {
|
|
418
|
+
const deleteEnabled = entity ? canDeleteEntity(collection, authController, fullPathToCollectionSegments(fullPath), entity) : true;
|
|
419
|
+
const actions: EntityAction[] = [editEntityAction];
|
|
420
|
+
if (createEnabled)
|
|
421
|
+
actions.push(copyEntityAction);
|
|
422
|
+
if (deleteEnabled)
|
|
423
|
+
actions.push(deleteEntityAction);
|
|
424
|
+
if (customEntityActions)
|
|
425
|
+
actions.push(...customEntityActions);
|
|
426
|
+
return actions;
|
|
427
|
+
}, [authController, collection, createEnabled, fullPath]);
|
|
428
|
+
|
|
429
|
+
const getIdColumnWidth = useCallback(() => {
|
|
430
|
+
const entityActions = getActionsForEntity({});
|
|
431
|
+
const collapsedActions = entityActions.filter(a => a.collapsed !== false);
|
|
432
|
+
const uncollapsedActions = entityActions.filter(a => a.collapsed === false);
|
|
433
|
+
const actionsWidth = uncollapsedActions.length * (largeLayout ? 40 : 30);
|
|
434
|
+
return (largeLayout ? (80 + actionsWidth) : (70 + actionsWidth)) + (collapsedActions.length > 0 ? (largeLayout ? 40 : 30) : 0);
|
|
435
|
+
}, [largeLayout, getActionsForEntity]);
|
|
436
|
+
|
|
435
437
|
const tableRowActionsBuilder = useCallback(({
|
|
436
438
|
entity,
|
|
437
439
|
size,
|
|
@@ -446,7 +448,7 @@ export const EntityCollectionView = React.memo(
|
|
|
446
448
|
|
|
447
449
|
const isSelected = isEntitySelected(entity);
|
|
448
450
|
|
|
449
|
-
const
|
|
451
|
+
const actions = getActionsForEntity({ entity, customEntityActions: collection.entityActions });
|
|
450
452
|
|
|
451
453
|
return (
|
|
452
454
|
<EntityCollectionRowActions
|
|
@@ -456,15 +458,18 @@ export const EntityCollectionView = React.memo(
|
|
|
456
458
|
isSelected={isSelected}
|
|
457
459
|
selectionEnabled={selectionEnabled}
|
|
458
460
|
size={size}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
461
|
+
highlightEntity={setSelectedNavigationEntity}
|
|
462
|
+
unhighlightEntity={unselectNavigatedEntity}
|
|
463
|
+
collection={collection}
|
|
464
|
+
fullPath={fullPath}
|
|
465
|
+
actions={actions}
|
|
463
466
|
hideId={collection?.hideIdFromCollection}
|
|
467
|
+
onCollectionChange={updateLastDeleteTimestamp}
|
|
468
|
+
selectionController={usedSelectionController}
|
|
464
469
|
/>
|
|
465
470
|
);
|
|
466
471
|
|
|
467
|
-
}, [isEntitySelected, collection, authController, fullPath, selectionEnabled, toggleEntitySelection,
|
|
472
|
+
}, [isEntitySelected, collection, authController, fullPath, selectionEnabled, toggleEntitySelection, createEnabled]);
|
|
468
473
|
|
|
469
474
|
const title = <Popover
|
|
470
475
|
open={popOverOpen}
|
|
@@ -522,6 +527,7 @@ export const EntityCollectionView = React.memo(
|
|
|
522
527
|
propertyKey={propertyKey}
|
|
523
528
|
property={property}
|
|
524
529
|
fullPath={fullPath}
|
|
530
|
+
collection={collection}
|
|
525
531
|
parentPathSegments={parentPathSegments ?? []}/>;
|
|
526
532
|
})}
|
|
527
533
|
</>;
|
|
@@ -569,10 +575,25 @@ export const EntityCollectionView = React.memo(
|
|
|
569
575
|
selectionEnabled={selectionEnabled}
|
|
570
576
|
collectionEntitiesCount={docsCount}
|
|
571
577
|
/>}
|
|
578
|
+
emptyComponent={canCreateEntities
|
|
579
|
+
? <div className="flex flex-col items-center justify-center">
|
|
580
|
+
<Typography variant={"subtitle2"}>So empty...</Typography>
|
|
581
|
+
<Button
|
|
582
|
+
color={"primary"}
|
|
583
|
+
variant={"outlined"}
|
|
584
|
+
onClick={onNewClick}
|
|
585
|
+
className="mt-4"
|
|
586
|
+
>
|
|
587
|
+
Create your first entity
|
|
588
|
+
</Button>
|
|
589
|
+
</div>
|
|
590
|
+
: undefined
|
|
591
|
+
}
|
|
572
592
|
hoverRow={hoverRow}
|
|
573
593
|
inlineEditing={checkInlineEditing()}
|
|
574
594
|
AdditionalHeaderWidget={buildAdditionalHeaderWidget}
|
|
575
595
|
AddColumnComponent={addColumnComponentInternal}
|
|
596
|
+
getIdColumnWidth={getIdColumnWidth}
|
|
576
597
|
additionalIDHeaderWidget={<EntityIdHeaderWidget
|
|
577
598
|
path={fullPath}
|
|
578
599
|
collection={collection}/>}
|
|
@@ -690,7 +711,10 @@ function buildPropertyWidthOverwrite(key: string, width: number): PartialEntityC
|
|
|
690
711
|
return { properties: { [key]: { columnWidth: width } } } as PartialEntityCollection;
|
|
691
712
|
}
|
|
692
713
|
|
|
693
|
-
function EntityIdHeaderWidget({
|
|
714
|
+
function EntityIdHeaderWidget({
|
|
715
|
+
collection,
|
|
716
|
+
path
|
|
717
|
+
}: {
|
|
694
718
|
collection: EntityCollection,
|
|
695
719
|
path: string
|
|
696
720
|
}) {
|
|
@@ -230,9 +230,9 @@ export function ReferenceSelectionInner<M extends Record<string, any>>(
|
|
|
230
230
|
size={size}
|
|
231
231
|
isSelected={isSelected}
|
|
232
232
|
selectionEnabled={multiselect}
|
|
233
|
-
toggleEntitySelection={toggleEntitySelection}
|
|
234
233
|
hideId={collection?.hideIdFromCollection}
|
|
235
|
-
|
|
234
|
+
fullPath={fullPath}
|
|
235
|
+
selectionController={selectionController}/>;
|
|
236
236
|
|
|
237
237
|
}, [multiselect, selectionController.selectedEntities, toggleEntitySelection, collection?.hideIdFromCollection]);
|
|
238
238
|
|
|
@@ -102,7 +102,7 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
102
102
|
onFilterUpdate,
|
|
103
103
|
sortBy,
|
|
104
104
|
error,
|
|
105
|
-
|
|
105
|
+
emptyComponent,
|
|
106
106
|
onSortByUpdate,
|
|
107
107
|
loading,
|
|
108
108
|
cellRenderer,
|
|
@@ -249,13 +249,11 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
249
249
|
if (loading)
|
|
250
250
|
return <CircularProgressCenter/>;
|
|
251
251
|
return <div
|
|
252
|
-
className="flex overflow-auto items-center justify-center p-2 gap-2 h-full">
|
|
252
|
+
className="flex flex-col overflow-auto items-center justify-center p-2 gap-2 h-full">
|
|
253
253
|
<AssignmentIcon/>
|
|
254
|
-
|
|
255
|
-
{emptyMessage}
|
|
256
|
-
</Typography>
|
|
254
|
+
{emptyComponent}
|
|
257
255
|
</div>;
|
|
258
|
-
}, [
|
|
256
|
+
}, [emptyComponent, loading]);
|
|
259
257
|
|
|
260
258
|
const empty = !loading && (data?.length ?? 0) === 0;
|
|
261
259
|
const customView = error ? buildErrorView() : (empty ? buildEmptyView() : undefined);
|