@firecms/core 3.0.0-alpha.73 → 3.0.0-alpha.75
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/internal/DeleteEntityDialog.d.ts → DeleteEntityDialog.d.ts} +1 -1
- package/dist/components/EntityCollectionTable/{internal/EntityCollectionRowActions.d.ts → EntityCollectionRowActions.d.ts} +3 -3
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +3 -3
- package/dist/components/EntityCollectionTable/{internal/PropertyTableCell.d.ts → PropertyTableCell.d.ts} +2 -2
- package/dist/components/EntityCollectionTable/SimpleEntityCollectionTable.d.ts +95 -0
- package/dist/components/EntityCollectionTable/column_utils.d.ts +16 -0
- package/dist/components/EntityCollectionTable/index.d.ts +4 -0
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -1
- package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
- package/dist/components/EntityCollectionTable/types.d.ts +2 -2
- package/dist/components/EntityCollectionTable/useDataSourceEntityCollectionTableController.d.ts +3 -3
- package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +2 -2
- package/dist/components/VirtualTable/VirtualTableCell.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +2 -3
- package/dist/components/VirtualTable/types.d.ts +1 -1
- package/dist/form/components/FieldHelperText.d.ts +1 -1
- package/dist/index.es.js +3505 -3304
- 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/types/collections.d.ts +2 -2
- package/dist/types/plugins.d.ts +2 -0
- package/package.json +3 -3
- package/src/components/{EntityCollectionTable/internal/DeleteEntityDialog.tsx → DeleteEntityDialog.tsx} +4 -4
- package/src/components/EntityCollectionTable/{internal/EntityCollectionRowActions.tsx → EntityCollectionRowActions.tsx} +4 -4
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +13 -48
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +3 -3
- package/src/components/EntityCollectionTable/{internal/PropertyTableCell.tsx → PropertyTableCell.tsx} +19 -18
- package/src/components/EntityCollectionTable/SimpleEntityCollectionTable.tsx +369 -0
- package/src/components/EntityCollectionTable/column_utils.tsx +74 -0
- package/src/components/EntityCollectionTable/index.tsx +4 -0
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +1 -2
- package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +18 -18
- package/src/components/EntityCollectionTable/types.tsx +2 -2
- package/src/components/EntityCollectionTable/useDataSourceEntityCollectionTableController.tsx +3 -3
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +4 -4
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +2 -2
- package/src/components/ReferenceSelectionInner.tsx +1 -1
- package/src/components/VirtualTable/VirtualTableCell.tsx +9 -13
- package/src/components/VirtualTable/VirtualTableProps.tsx +3 -4
- package/src/components/VirtualTable/types.tsx +1 -1
- package/src/core/FireCMS.tsx +2 -2
- package/src/form/EntityForm.tsx +2 -4
- package/src/form/PropertyFieldBinding.tsx +2 -0
- package/src/form/components/FieldHelperText.tsx +2 -2
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +2 -1
- package/src/form/field_bindings/MapFieldBinding.tsx +3 -9
- package/src/internal/useBuildSideEntityController.tsx +1 -0
- package/src/preview/components/BooleanPreview.tsx +1 -1
- package/src/types/collections.ts +2 -2
- package/src/types/plugins.tsx +4 -2
|
@@ -276,7 +276,7 @@ export interface CollectionActionsProps<M extends Record<string, any> = any, Use
|
|
|
276
276
|
* Use this controller to get the table controller and to update the
|
|
277
277
|
* table controller state.
|
|
278
278
|
*/
|
|
279
|
-
tableController:
|
|
279
|
+
tableController: EntityTableController<M>;
|
|
280
280
|
/**
|
|
281
281
|
* Context of the app status
|
|
282
282
|
*/
|
|
@@ -421,7 +421,7 @@ export type DefaultSelectedViewParams = {
|
|
|
421
421
|
/**
|
|
422
422
|
* You can use this controller to control the table view of a collection.
|
|
423
423
|
*/
|
|
424
|
-
export type
|
|
424
|
+
export type EntityTableController<M extends Record<string, any> = any> = {
|
|
425
425
|
data: Entity<M>[];
|
|
426
426
|
dataLoading: boolean;
|
|
427
427
|
noMoreToLoad: boolean;
|
package/dist/types/plugins.d.ts
CHANGED
|
@@ -101,11 +101,13 @@ export type FireCMSPlugin<PROPS = any, FORM_PROPS = any, EC extends EntityCollec
|
|
|
101
101
|
context: FireCMSContext;
|
|
102
102
|
path: string;
|
|
103
103
|
collection: EC;
|
|
104
|
+
parentCollectionIds?: string[];
|
|
104
105
|
}) => boolean;
|
|
105
106
|
onTextSearchClick?: (props: {
|
|
106
107
|
context: FireCMSContext;
|
|
107
108
|
path: string;
|
|
108
109
|
collection: EC;
|
|
110
|
+
parentCollectionIds?: string[];
|
|
109
111
|
}) => Promise<boolean>;
|
|
110
112
|
/**
|
|
111
113
|
* Use this method to inject widgets to the entity collections header
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.0-alpha.
|
|
4
|
+
"version": "3.0.0-alpha.75",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"generateIcons": "ts-node --esm src/icons/generateIcons.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@firecms/ui": "^3.0.0-alpha.
|
|
41
|
+
"@firecms/ui": "^3.0.0-alpha.75",
|
|
42
42
|
"@fontsource/ibm-plex-mono": "^5.0.8",
|
|
43
43
|
"@fontsource/roboto": "^5.0.8",
|
|
44
44
|
"@hello-pangea/dnd": "^16.5.0",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"dist",
|
|
108
108
|
"src"
|
|
109
109
|
],
|
|
110
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "7d04e9e7855d1720e2b394091863dc3637a87712",
|
|
111
111
|
"publishConfig": {
|
|
112
112
|
"access": "public"
|
|
113
113
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Entity, EntityCallbacks, EntityCollection } from "
|
|
1
|
+
import { Entity, EntityCallbacks, EntityCollection } from "../types";
|
|
2
2
|
import React, { useCallback, useMemo, useState } from "react";
|
|
3
|
-
import { deleteEntityWithCallbacks, useDataSource, useFireCMSContext, useSnackbarController } from "../../../hooks";
|
|
4
3
|
import { Button, CircularProgress, Dialog, DialogActions, DialogContent, Typography } from "@firecms/ui";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { deleteEntityWithCallbacks, useDataSource, useFireCMSContext, useSnackbarController } from "../hooks";
|
|
5
|
+
import { resolveCollection } from "../util";
|
|
6
|
+
import { EntityPreview } from "./EntityPreview";
|
|
7
7
|
|
|
8
8
|
export interface DeleteEntityDialogProps<M extends Record<string, any>> {
|
|
9
9
|
entityOrEntitiesToDelete?: Entity<M> | Entity<M>[],
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { MouseEvent, useCallback } from "react";
|
|
2
2
|
import equal from "react-fast-compare"
|
|
3
3
|
|
|
4
|
-
import { CollectionSize, Entity, EntityAction, EntityCollection, SelectionController } from "
|
|
4
|
+
import { CollectionSize, Entity, EntityAction, EntityCollection, SelectionController } from "../../types";
|
|
5
5
|
import { Checkbox, cn, IconButton, Menu, MenuItem, MoreVertIcon, Skeleton, Tooltip, Typography } from "@firecms/ui";
|
|
6
|
-
import { useFireCMSContext, useLargeLayout } from "
|
|
6
|
+
import { useFireCMSContext, useLargeLayout } from "../../hooks";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
*
|
|
@@ -45,7 +45,7 @@ export const EntityCollectionRowActions = React.memo(function EntityCollectionRo
|
|
|
45
45
|
width: number,
|
|
46
46
|
frozen?: boolean,
|
|
47
47
|
size: CollectionSize,
|
|
48
|
-
isSelected
|
|
48
|
+
isSelected?: boolean,
|
|
49
49
|
selectionEnabled?: boolean,
|
|
50
50
|
actions?: EntityAction[],
|
|
51
51
|
hideId?: boolean,
|
|
@@ -145,7 +145,7 @@ export const EntityCollectionRowActions = React.memo(function EntityCollectionRo
|
|
|
145
145
|
<Tooltip title={`Select ${entity.id}`}>
|
|
146
146
|
<Checkbox
|
|
147
147
|
size={largeLayout ? "medium" : "small"}
|
|
148
|
-
checked={isSelected}
|
|
148
|
+
checked={Boolean(isSelected)}
|
|
149
149
|
onCheckedChange={onCheckedChange}
|
|
150
150
|
/>
|
|
151
151
|
</Tooltip>}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useContext, useEffect, useMemo } from "react";
|
|
2
2
|
import equal from "react-fast-compare";
|
|
3
|
-
import { getTableCellAlignment, getTablePropertyColumnWidth } from "./internal/common";
|
|
4
3
|
import {
|
|
5
4
|
AdditionalFieldDelegate,
|
|
6
5
|
CollectionSize,
|
|
@@ -11,13 +10,13 @@ import {
|
|
|
11
10
|
SelectedCellProps,
|
|
12
11
|
User
|
|
13
12
|
} from "../../types";
|
|
14
|
-
import { PropertyTableCell } from "./
|
|
13
|
+
import { PropertyTableCell } from "./PropertyTableCell";
|
|
15
14
|
import { ErrorBoundary } from "../ErrorBoundary";
|
|
16
15
|
import { useFireCMSContext, useLargeLayout } from "../../hooks";
|
|
17
16
|
import { CellRendererParams, VirtualTable, VirtualTableColumn } from "../VirtualTable";
|
|
18
|
-
import { enumToObjectEntries,
|
|
17
|
+
import { enumToObjectEntries, getValueInPath } from "../../util";
|
|
19
18
|
import { getRowHeight } from "../VirtualTable/common";
|
|
20
|
-
import { EntityCollectionRowActions } from "./
|
|
19
|
+
import { EntityCollectionRowActions } from "./EntityCollectionRowActions";
|
|
21
20
|
import { EntityCollectionTableController } from "./types";
|
|
22
21
|
import { CollectionTableToolbar } from "./internal/CollectionTableToolbar";
|
|
23
22
|
import { EntityCollectionTableProps } from "./EntityCollectionTableProps";
|
|
@@ -27,9 +26,9 @@ import { ReferenceFilterField } from "./filters/ReferenceFilterField";
|
|
|
27
26
|
import { StringNumberFilterField } from "./filters/StringNumberFilterField";
|
|
28
27
|
import { BooleanFilterField } from "./filters/BooleanFilterField";
|
|
29
28
|
import { DateTimeFilterField } from "./filters/DateTimeFilterField";
|
|
30
|
-
import { getColumnKeysForProperty } from "../EntityCollectionView/useColumnsIds";
|
|
31
29
|
import { CustomFieldValidator } from "../../form/validation";
|
|
32
30
|
import { renderSkeletonText } from "../../preview";
|
|
31
|
+
import { propertiesToColumns } from "./column_utils";
|
|
33
32
|
|
|
34
33
|
const DEFAULT_STATE = {} as any;
|
|
35
34
|
|
|
@@ -52,7 +51,7 @@ export const useEntityCollectionTableController = () => useContext<EntityCollect
|
|
|
52
51
|
* options you see in collections in the top level navigation, you can
|
|
53
52
|
* check {@link EntityCollectionView}.
|
|
54
53
|
*
|
|
55
|
-
* The data displayed in the table is managed by a {@link
|
|
54
|
+
* The data displayed in the table is managed by a {@link EntityTableController}.
|
|
56
55
|
* You can build the default, bound to a path in the datasource, by using the hook
|
|
57
56
|
* {@link useDataSourceEntityCollectionTableController}
|
|
58
57
|
*
|
|
@@ -283,34 +282,13 @@ export const EntityCollectionTable = React.memo<EntityCollectionTableProps<any>>
|
|
|
283
282
|
}, [additionalFieldsMap, size, selectedEntityIds]);
|
|
284
283
|
|
|
285
284
|
const collectionColumns: VirtualTableColumn[] = useMemo(() => {
|
|
286
|
-
const columnsResult: VirtualTableColumn[] =
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (!property)
|
|
294
|
-
throw Error("Internal error: no property found in path " + key);
|
|
295
|
-
const filterable = filterableProperty(property);
|
|
296
|
-
return {
|
|
297
|
-
key: key as string,
|
|
298
|
-
align: getTableCellAlignment(property),
|
|
299
|
-
icon: (hoverOrOpen) => getIconForProperty(property, "small"),
|
|
300
|
-
title: property.name ?? key as string,
|
|
301
|
-
sortable: sortable && (forceFilter ? Object.keys(forceFilter).includes(key) : true),
|
|
302
|
-
filter: !disabledFilterChange && filterable,
|
|
303
|
-
width: getTablePropertyColumnWidth(property),
|
|
304
|
-
resizable: true,
|
|
305
|
-
custom: {
|
|
306
|
-
resolvedProperty: property,
|
|
307
|
-
disabled
|
|
308
|
-
},
|
|
309
|
-
AdditionalHeaderWidget: ({ onHover }) => AdditionalHeaderWidget
|
|
310
|
-
? <AdditionalHeaderWidget property={property} propertyKey={key} onHover={onHover}/>
|
|
311
|
-
: undefined
|
|
312
|
-
};
|
|
313
|
-
});
|
|
285
|
+
const columnsResult: VirtualTableColumn[] = propertiesToColumns({
|
|
286
|
+
properties,
|
|
287
|
+
sortable,
|
|
288
|
+
forceFilter,
|
|
289
|
+
disabledFilter: disabledFilterChange,
|
|
290
|
+
AdditionalHeaderWidget
|
|
291
|
+
});
|
|
314
292
|
|
|
315
293
|
const additionalTableColumns: VirtualTableColumn[] = additionalFields
|
|
316
294
|
? additionalFields.map((additionalField) =>
|
|
@@ -384,7 +362,7 @@ export const EntityCollectionTable = React.memo<EntityCollectionTableProps<any>>
|
|
|
384
362
|
select,
|
|
385
363
|
onValueChange,
|
|
386
364
|
size,
|
|
387
|
-
selectedCell
|
|
365
|
+
selectedCell
|
|
388
366
|
}}
|
|
389
367
|
>
|
|
390
368
|
|
|
@@ -504,16 +482,3 @@ function createFilterField({
|
|
|
504
482
|
<div>{`Currently the filter field ${resolvedProperty.dataType} is not supported`}</div>
|
|
505
483
|
);
|
|
506
484
|
}
|
|
507
|
-
|
|
508
|
-
function filterableProperty(property: ResolvedProperty, partOfArray = false): boolean {
|
|
509
|
-
if (partOfArray) {
|
|
510
|
-
return ["string", "number", "date", "reference"].includes(property.dataType);
|
|
511
|
-
}
|
|
512
|
-
if (property.dataType === "array") {
|
|
513
|
-
if (property.of)
|
|
514
|
-
return filterableProperty(property.of, true);
|
|
515
|
-
else
|
|
516
|
-
return false;
|
|
517
|
-
}
|
|
518
|
-
return ["string", "number", "boolean", "date", "reference", "array"].includes(property.dataType);
|
|
519
|
-
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ResolvedProperties,
|
|
8
8
|
ResolvedProperty,
|
|
9
9
|
SelectionController,
|
|
10
|
-
|
|
10
|
+
EntityTableController,
|
|
11
11
|
User
|
|
12
12
|
} from "../../types";
|
|
13
13
|
import { OnCellValueChange, UniqueFieldValidator } from "./types";
|
|
@@ -93,9 +93,9 @@ export type EntityCollectionTableProps<M extends Record<string, any>,
|
|
|
93
93
|
/**
|
|
94
94
|
* Controller holding the logic for the table
|
|
95
95
|
* {@link useDataSourceEntityCollectionTableController}
|
|
96
|
-
* {@link
|
|
96
|
+
* {@link EntityTableController}
|
|
97
97
|
*/
|
|
98
|
-
tableController:
|
|
98
|
+
tableController: EntityTableController<M>;
|
|
99
99
|
|
|
100
100
|
displayedColumnIds: PropertyColumnConfig[];
|
|
101
101
|
|
|
@@ -8,28 +8,29 @@ import {
|
|
|
8
8
|
ResolvedNumberProperty,
|
|
9
9
|
ResolvedProperty,
|
|
10
10
|
ResolvedStringProperty
|
|
11
|
-
} from "
|
|
11
|
+
} from "../../types";
|
|
12
12
|
|
|
13
|
-
import { VirtualTableInput } from "
|
|
14
|
-
import { VirtualTableSelect } from "
|
|
15
|
-
import { VirtualTableNumberInput } from "
|
|
16
|
-
import { VirtualTableSwitch } from "
|
|
17
|
-
import { VirtualTableDateField } from "
|
|
13
|
+
import { VirtualTableInput } from "../VirtualTable/fields/VirtualTableInput";
|
|
14
|
+
import { VirtualTableSelect } from "../VirtualTable/fields/VirtualTableSelect";
|
|
15
|
+
import { VirtualTableNumberInput } from "../VirtualTable/fields/VirtualTableNumberInput";
|
|
16
|
+
import { VirtualTableSwitch } from "../VirtualTable/fields/VirtualTableSwitch";
|
|
17
|
+
import { VirtualTableDateField } from "../VirtualTable/fields/VirtualTableDateField";
|
|
18
18
|
|
|
19
|
-
import { TableStorageUpload } from "
|
|
20
|
-
import { TableReferenceField } from "
|
|
19
|
+
import { TableStorageUpload } from "./fields/TableStorageUpload";
|
|
20
|
+
import { TableReferenceField } from "./fields/TableReferenceField";
|
|
21
21
|
|
|
22
|
-
import { PropertyPreview } from "
|
|
23
|
-
import { getPreviewSizeFrom } from "
|
|
24
|
-
import { isReadOnly } from "
|
|
22
|
+
import { PropertyPreview } from "../../preview";
|
|
23
|
+
import { getPreviewSizeFrom } from "../../preview/util";
|
|
24
|
+
import { isReadOnly } from "../../util";
|
|
25
25
|
|
|
26
|
-
import { CustomFieldValidator, mapPropertyToYup } from "
|
|
27
|
-
import { useEntityCollectionTableController } from "
|
|
28
|
-
import { useClearRestoreValue, useFireCMSContext } from "
|
|
26
|
+
import { CustomFieldValidator, mapPropertyToYup } from "../../form/validation";
|
|
27
|
+
import { useEntityCollectionTableController } from "./EntityCollectionTable";
|
|
28
|
+
import { useClearRestoreValue, useFireCMSContext } from "../../hooks";
|
|
29
29
|
|
|
30
|
-
import { EntityTableCell } from "./EntityTableCell";
|
|
31
|
-
import {
|
|
32
|
-
|
|
30
|
+
import { EntityTableCell } from "./internal/EntityTableCell";
|
|
31
|
+
import { EntityTableCellActions } from "./internal/EntityTableCellActions";
|
|
32
|
+
|
|
33
|
+
import { getRowHeight } from "../VirtualTable/common";
|
|
33
34
|
|
|
34
35
|
export interface PropertyTableCellProps<T extends CMSType, M extends Record<string, any>> {
|
|
35
36
|
propertyKey: string;
|
|
@@ -46,7 +47,7 @@ export interface PropertyTableCellProps<T extends CMSType, M extends Record<stri
|
|
|
46
47
|
disabled: boolean;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
function isStorageProperty
|
|
50
|
+
function isStorageProperty(property: ResolvedProperty) {
|
|
50
51
|
if (property.dataType === "string" && (property as ResolvedStringProperty).storage)
|
|
51
52
|
return true;
|
|
52
53
|
if (property.dataType === "array") {
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import React, { useCallback, useEffect } from "react";
|
|
2
|
+
import equal from "react-fast-compare";
|
|
3
|
+
import {
|
|
4
|
+
CollectionSize,
|
|
5
|
+
Entity,
|
|
6
|
+
EntityTableController,
|
|
7
|
+
FilterValues,
|
|
8
|
+
ResolvedProperty,
|
|
9
|
+
SelectedCellProps,
|
|
10
|
+
User
|
|
11
|
+
} from "../../types";
|
|
12
|
+
import { useLargeLayout } from "../../hooks";
|
|
13
|
+
import { CellRendererParams, VirtualTable, VirtualTableColumn } from "../VirtualTable";
|
|
14
|
+
import { enumToObjectEntries } from "../../util";
|
|
15
|
+
import { OnCellValueChange } from "./types";
|
|
16
|
+
import { CollectionTableToolbar } from "./internal/CollectionTableToolbar";
|
|
17
|
+
import { EntityCollectionTableProps, OnColumnResizeParams } from "./EntityCollectionTableProps";
|
|
18
|
+
import { FilterFormFieldProps } from "../VirtualTable/VirtualTableHeader";
|
|
19
|
+
import { ReferenceFilterField } from "./filters/ReferenceFilterField";
|
|
20
|
+
import { StringNumberFilterField } from "./filters/StringNumberFilterField";
|
|
21
|
+
import { BooleanFilterField } from "./filters/BooleanFilterField";
|
|
22
|
+
import { DateTimeFilterField } from "./filters/DateTimeFilterField";
|
|
23
|
+
import { EntityCollectionTableContext } from "./EntityCollectionTable";
|
|
24
|
+
|
|
25
|
+
export type SimpleEntityCollectionTableProps<M extends Record<string, any>> = {
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* List of entities that will be displayed as selected;
|
|
29
|
+
*/
|
|
30
|
+
highlightedEntities?: Entity<M>[];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Override the title in the toolbar
|
|
34
|
+
*/
|
|
35
|
+
title?: React.ReactNode;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Callback when a cell value changes.
|
|
39
|
+
*/
|
|
40
|
+
onValueChange?: OnCellValueChange<any, M>;
|
|
41
|
+
|
|
42
|
+
columns: VirtualTableColumn[];
|
|
43
|
+
|
|
44
|
+
cellRenderer: React.ComponentType<CellRendererParams<Entity<M>>>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Builder for creating the buttons in each row
|
|
48
|
+
* @param entity
|
|
49
|
+
* @param size
|
|
50
|
+
*/
|
|
51
|
+
tableRowActionsBuilder?: (params: {
|
|
52
|
+
entity: Entity<M>,
|
|
53
|
+
size: CollectionSize,
|
|
54
|
+
width: number,
|
|
55
|
+
frozen?: boolean
|
|
56
|
+
}) => React.ReactNode;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Callback when anywhere on the table is clicked
|
|
60
|
+
*/
|
|
61
|
+
onEntityClick?(entity: Entity<M>): void;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Callback when a column is resized
|
|
65
|
+
*/
|
|
66
|
+
onColumnResize?(params: OnColumnResizeParams): void;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Callback when the selected size of the table is changed
|
|
70
|
+
*/
|
|
71
|
+
onSizeChanged?(size: CollectionSize): void;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Should apply a different style to a row when hovering
|
|
75
|
+
*/
|
|
76
|
+
hoverRow?: boolean;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Additional component that renders actions such as buttons in the
|
|
80
|
+
* collection toolbar, displayed on the left side
|
|
81
|
+
*/
|
|
82
|
+
actionsStart?: React.ReactNode;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Additional component that renders actions such as buttons in the
|
|
86
|
+
* collection toolbar, displayed on the right side
|
|
87
|
+
*/
|
|
88
|
+
actions?: React.ReactNode;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Controller holding the logic for the table
|
|
92
|
+
* {@link useDataSourceEntityCollectionTableController}
|
|
93
|
+
* {@link EntityTableController}
|
|
94
|
+
*/
|
|
95
|
+
tableController: EntityTableController<M>;
|
|
96
|
+
|
|
97
|
+
filterable?: boolean;
|
|
98
|
+
|
|
99
|
+
sortable?: boolean;
|
|
100
|
+
|
|
101
|
+
inlineEditing?: boolean;
|
|
102
|
+
|
|
103
|
+
textSearchEnabled?: boolean;
|
|
104
|
+
|
|
105
|
+
emptyComponent?: React.ReactNode;
|
|
106
|
+
|
|
107
|
+
forceFilter?: FilterValues<keyof M extends string ? keyof M : never>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* This component is in charge of rendering a collection table with a high
|
|
114
|
+
* degree of customization.
|
|
115
|
+
*
|
|
116
|
+
* This component is used internally by {@link EntityCollectionView} and
|
|
117
|
+
* {@link useReferenceDialog}
|
|
118
|
+
*
|
|
119
|
+
* Please note that you only need to use this component if you are building
|
|
120
|
+
* a custom view. If you just need to create a default view you can do it
|
|
121
|
+
* exclusively with config options.
|
|
122
|
+
*
|
|
123
|
+
* If you want to bind a {@link EntityCollection} to a table with the default
|
|
124
|
+
* options you see in collections in the top level navigation, you can
|
|
125
|
+
* check {@link EntityCollectionView}.
|
|
126
|
+
*
|
|
127
|
+
* The data displayed in the table is managed by a {@link EntityTableController}.
|
|
128
|
+
* You can build the default, bound to a path in the datasource, by using the hook
|
|
129
|
+
* {@link useDataSourceEntityCollectionTableController}
|
|
130
|
+
*
|
|
131
|
+
* @see EntityCollectionTableProps
|
|
132
|
+
* @see EntityCollectionView
|
|
133
|
+
* @see VirtualTable
|
|
134
|
+
* @group Components
|
|
135
|
+
*/
|
|
136
|
+
export const SimpleEntityCollectionTable = React.memo<SimpleEntityCollectionTableProps<any>>(
|
|
137
|
+
function SimpleEntityCollectionTable<M extends Record<string, any>, UserType extends User>
|
|
138
|
+
({
|
|
139
|
+
actionsStart,
|
|
140
|
+
actions,
|
|
141
|
+
title,
|
|
142
|
+
onValueChange,
|
|
143
|
+
cellRenderer,
|
|
144
|
+
highlightedEntities,
|
|
145
|
+
onEntityClick,
|
|
146
|
+
onColumnResize,
|
|
147
|
+
onSizeChanged,
|
|
148
|
+
hoverRow = true,
|
|
149
|
+
inlineEditing = false,
|
|
150
|
+
tableController:
|
|
151
|
+
{
|
|
152
|
+
data,
|
|
153
|
+
dataLoading,
|
|
154
|
+
noMoreToLoad,
|
|
155
|
+
dataLoadingError,
|
|
156
|
+
filterValues,
|
|
157
|
+
setFilterValues,
|
|
158
|
+
sortBy,
|
|
159
|
+
setSortBy,
|
|
160
|
+
setSearchString,
|
|
161
|
+
clearFilter,
|
|
162
|
+
itemCount,
|
|
163
|
+
setItemCount,
|
|
164
|
+
pageSize = 50,
|
|
165
|
+
paginationEnabled,
|
|
166
|
+
checkFilterCombination,
|
|
167
|
+
setPopupCell
|
|
168
|
+
},
|
|
169
|
+
filterable = true,
|
|
170
|
+
sortable = true,
|
|
171
|
+
emptyComponent,
|
|
172
|
+
textSearchEnabled,
|
|
173
|
+
columns,
|
|
174
|
+
forceFilter
|
|
175
|
+
}: SimpleEntityCollectionTableProps<M>) {
|
|
176
|
+
|
|
177
|
+
const largeLayout = useLargeLayout();
|
|
178
|
+
|
|
179
|
+
const [size, setSize] = React.useState<CollectionSize>("m");
|
|
180
|
+
|
|
181
|
+
const [selectedCell, setSelectedCell] = React.useState<SelectedCellProps<M> | undefined>(undefined);
|
|
182
|
+
|
|
183
|
+
const filterIsSet = !!filterValues && Object.keys(filterValues).length > 0;
|
|
184
|
+
|
|
185
|
+
const loadNextPage = () => {
|
|
186
|
+
if (!paginationEnabled || dataLoading || noMoreToLoad)
|
|
187
|
+
return;
|
|
188
|
+
if (itemCount !== undefined)
|
|
189
|
+
setItemCount?.(itemCount + pageSize);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const resetPagination = useCallback(() => {
|
|
193
|
+
setItemCount?.(pageSize);
|
|
194
|
+
}, [pageSize]);
|
|
195
|
+
|
|
196
|
+
const onRowClick = useCallback(({ rowData }: {
|
|
197
|
+
rowData: Entity<M>
|
|
198
|
+
}) => {
|
|
199
|
+
console.debug("EntityCollectionTable click");
|
|
200
|
+
if (inlineEditing)
|
|
201
|
+
return;
|
|
202
|
+
return onEntityClick && onEntityClick(rowData);
|
|
203
|
+
}, [onEntityClick, inlineEditing]);
|
|
204
|
+
|
|
205
|
+
const updateSize = useCallback((size: CollectionSize) => {
|
|
206
|
+
if (onSizeChanged)
|
|
207
|
+
onSizeChanged(size);
|
|
208
|
+
setSize(size);
|
|
209
|
+
}, []);
|
|
210
|
+
|
|
211
|
+
const onTextSearch = useCallback((newSearchString?: string) => setSearchString?.(newSearchString), []);
|
|
212
|
+
|
|
213
|
+
// on ESC key press
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
const escFunction = (event: any) => {
|
|
216
|
+
if (event.keyCode === 27) {
|
|
217
|
+
unselect();
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
document.addEventListener("keydown", escFunction, false);
|
|
221
|
+
return () => {
|
|
222
|
+
document.removeEventListener("keydown", escFunction, false);
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const select = useCallback((cell?: SelectedCellProps<M>) => {
|
|
227
|
+
setSelectedCell(cell);
|
|
228
|
+
}, []);
|
|
229
|
+
|
|
230
|
+
const unselect = useCallback(() => {
|
|
231
|
+
setSelectedCell(undefined);
|
|
232
|
+
}, []);
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
const onFilterUpdate = useCallback((updatedFilterValues?: FilterValues<any>) => {
|
|
236
|
+
setFilterValues?.({ ...updatedFilterValues, ...forceFilter } as FilterValues<any>);
|
|
237
|
+
}, [forceFilter]);
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<EntityCollectionTableContext.Provider
|
|
241
|
+
value={{
|
|
242
|
+
setPopupCell: setPopupCell as ((cell?: SelectedCellProps<M>) => void),
|
|
243
|
+
select,
|
|
244
|
+
onValueChange,
|
|
245
|
+
size,
|
|
246
|
+
selectedCell,
|
|
247
|
+
}}
|
|
248
|
+
>
|
|
249
|
+
<div className="h-full w-full flex flex-col bg-white dark:bg-gray-950">
|
|
250
|
+
|
|
251
|
+
<CollectionTableToolbar
|
|
252
|
+
filterIsSet={filterIsSet}
|
|
253
|
+
onTextSearch={textSearchEnabled ? onTextSearch : undefined}
|
|
254
|
+
clearFilter={clearFilter}
|
|
255
|
+
size={size}
|
|
256
|
+
onSizeChanged={updateSize}
|
|
257
|
+
title={title}
|
|
258
|
+
actionsStart={actionsStart}
|
|
259
|
+
actions={actions}
|
|
260
|
+
loading={dataLoading}/>
|
|
261
|
+
|
|
262
|
+
<VirtualTable
|
|
263
|
+
data={data}
|
|
264
|
+
columns={columns}
|
|
265
|
+
cellRenderer={cellRenderer}
|
|
266
|
+
onRowClick={inlineEditing ? undefined : (onEntityClick ? onRowClick : undefined)}
|
|
267
|
+
onEndReached={loadNextPage}
|
|
268
|
+
onResetPagination={resetPagination}
|
|
269
|
+
error={dataLoadingError}
|
|
270
|
+
paginationEnabled={paginationEnabled}
|
|
271
|
+
onColumnResize={onColumnResize}
|
|
272
|
+
size={size}
|
|
273
|
+
loading={dataLoading}
|
|
274
|
+
filter={filterValues}
|
|
275
|
+
onFilterUpdate={setFilterValues ? onFilterUpdate : undefined}
|
|
276
|
+
sortBy={sortBy}
|
|
277
|
+
onSortByUpdate={setSortBy as ((sortBy?: [string, "asc" | "desc"]) => void)}
|
|
278
|
+
hoverRow={hoverRow}
|
|
279
|
+
emptyComponent={emptyComponent}
|
|
280
|
+
checkFilterCombination={checkFilterCombination}
|
|
281
|
+
createFilterField={filterable ? createFilterField : undefined}
|
|
282
|
+
className="flex-grow"
|
|
283
|
+
/>
|
|
284
|
+
|
|
285
|
+
</div>
|
|
286
|
+
</EntityCollectionTableContext.Provider>
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
},
|
|
290
|
+
equal
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
function createFilterField({
|
|
294
|
+
id,
|
|
295
|
+
filterValue,
|
|
296
|
+
setFilterValue,
|
|
297
|
+
column,
|
|
298
|
+
hidden,
|
|
299
|
+
setHidden
|
|
300
|
+
}: FilterFormFieldProps<{
|
|
301
|
+
resolvedProperty: ResolvedProperty,
|
|
302
|
+
disabled: boolean,
|
|
303
|
+
}>): React.ReactNode {
|
|
304
|
+
|
|
305
|
+
if (!column.custom) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const { resolvedProperty } = column.custom;
|
|
310
|
+
|
|
311
|
+
const isArray = resolvedProperty?.dataType === "array";
|
|
312
|
+
const baseProperty: ResolvedProperty = isArray ? resolvedProperty.of : resolvedProperty;
|
|
313
|
+
if (!baseProperty) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
if (baseProperty.dataType === "reference") {
|
|
317
|
+
return <ReferenceFilterField value={filterValue}
|
|
318
|
+
setValue={setFilterValue}
|
|
319
|
+
name={id as string}
|
|
320
|
+
isArray={isArray}
|
|
321
|
+
path={baseProperty.path}
|
|
322
|
+
title={resolvedProperty?.name}
|
|
323
|
+
previewProperties={baseProperty?.previewProperties}
|
|
324
|
+
hidden={hidden}
|
|
325
|
+
setHidden={setHidden}/>;
|
|
326
|
+
} else if (baseProperty.dataType === "number" || baseProperty.dataType === "string") {
|
|
327
|
+
const name = baseProperty.name;
|
|
328
|
+
const enumValues = baseProperty.enumValues ? enumToObjectEntries(baseProperty.enumValues) : undefined;
|
|
329
|
+
return <StringNumberFilterField value={filterValue}
|
|
330
|
+
setValue={setFilterValue}
|
|
331
|
+
name={id as string}
|
|
332
|
+
dataType={baseProperty.dataType}
|
|
333
|
+
isArray={isArray}
|
|
334
|
+
enumValues={enumValues}
|
|
335
|
+
title={name}/>;
|
|
336
|
+
} else if (baseProperty.dataType === "boolean") {
|
|
337
|
+
const name = baseProperty.name;
|
|
338
|
+
return <BooleanFilterField value={filterValue}
|
|
339
|
+
setValue={setFilterValue}
|
|
340
|
+
name={id as string}
|
|
341
|
+
title={name}/>;
|
|
342
|
+
|
|
343
|
+
} else if (baseProperty.dataType === "date") {
|
|
344
|
+
const title = baseProperty.name;
|
|
345
|
+
return <DateTimeFilterField value={filterValue}
|
|
346
|
+
setValue={setFilterValue}
|
|
347
|
+
name={id as string}
|
|
348
|
+
mode={baseProperty.mode}
|
|
349
|
+
isArray={isArray}
|
|
350
|
+
title={title}/>;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return (
|
|
354
|
+
<div>{`Currently the filter field ${resolvedProperty.dataType} is not supported`}</div>
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function filterableProperty(property: ResolvedProperty, partOfArray = false): boolean {
|
|
359
|
+
if (partOfArray) {
|
|
360
|
+
return ["string", "number", "date", "reference"].includes(property.dataType);
|
|
361
|
+
}
|
|
362
|
+
if (property.dataType === "array") {
|
|
363
|
+
if (property.of)
|
|
364
|
+
return filterableProperty(property.of, true);
|
|
365
|
+
else
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
return ["string", "number", "boolean", "date", "reference", "array"].includes(property.dataType);
|
|
369
|
+
}
|