@firecms/core 3.0.0-alpha.72 → 3.0.0-alpha.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/components/{EntityCollectionTable/internal/DeleteEntityDialog.d.ts → DeleteEntityDialog.d.ts} +1 -1
  2. package/dist/components/EntityCollectionTable/{internal/EntityCollectionRowActions.d.ts → EntityCollectionRowActions.d.ts} +3 -3
  3. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  4. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +3 -3
  5. package/dist/components/EntityCollectionTable/{internal/PropertyTableCell.d.ts → PropertyTableCell.d.ts} +2 -2
  6. package/dist/components/EntityCollectionTable/SimpleEntityCollectionTable.d.ts +95 -0
  7. package/dist/components/EntityCollectionTable/column_utils.d.ts +16 -0
  8. package/dist/components/EntityCollectionTable/index.d.ts +4 -0
  9. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -1
  10. package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +1 -1
  11. package/dist/components/EntityCollectionTable/types.d.ts +2 -2
  12. package/dist/components/EntityCollectionTable/useDataSourceEntityCollectionTableController.d.ts +3 -3
  13. package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +2 -2
  14. package/dist/components/VirtualTable/VirtualTableCell.d.ts +1 -1
  15. package/dist/components/VirtualTable/VirtualTableProps.d.ts +2 -3
  16. package/dist/components/VirtualTable/types.d.ts +1 -1
  17. package/dist/form/components/FieldHelperText.d.ts +1 -1
  18. package/dist/index.es.js +5004 -4214
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.umd.js +587 -4
  21. package/dist/index.umd.js.map +1 -1
  22. package/dist/types/collections.d.ts +2 -2
  23. package/dist/types/plugins.d.ts +2 -0
  24. package/package.json +3 -3
  25. package/src/components/{EntityCollectionTable/internal/DeleteEntityDialog.tsx → DeleteEntityDialog.tsx} +4 -4
  26. package/src/components/EntityCollectionTable/{internal/EntityCollectionRowActions.tsx → EntityCollectionRowActions.tsx} +4 -4
  27. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +13 -48
  28. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +3 -3
  29. package/src/components/EntityCollectionTable/{internal/PropertyTableCell.tsx → PropertyTableCell.tsx} +19 -18
  30. package/src/components/EntityCollectionTable/SimpleEntityCollectionTable.tsx +369 -0
  31. package/src/components/EntityCollectionTable/column_utils.tsx +74 -0
  32. package/src/components/EntityCollectionTable/index.tsx +4 -0
  33. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
  34. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +1 -2
  35. package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +1 -1
  36. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +18 -18
  37. package/src/components/EntityCollectionTable/types.tsx +2 -2
  38. package/src/components/EntityCollectionTable/useDataSourceEntityCollectionTableController.tsx +3 -3
  39. package/src/components/EntityCollectionView/EntityCollectionView.tsx +4 -4
  40. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +2 -2
  41. package/src/components/ReferenceSelectionInner.tsx +1 -1
  42. package/src/components/VirtualTable/VirtualTableCell.tsx +9 -13
  43. package/src/components/VirtualTable/VirtualTableProps.tsx +3 -4
  44. package/src/components/VirtualTable/VirtualTableRow.tsx +0 -1
  45. package/src/components/VirtualTable/types.tsx +1 -1
  46. package/src/core/EntitySidePanel.tsx +0 -1
  47. package/src/core/FireCMS.tsx +2 -2
  48. package/src/form/EntityForm.tsx +7 -8
  49. package/src/form/PropertyFieldBinding.tsx +3 -1
  50. package/src/form/components/FieldHelperText.tsx +2 -2
  51. package/src/form/field_bindings/DateTimeFieldBinding.tsx +2 -1
  52. package/src/form/field_bindings/MapFieldBinding.tsx +3 -9
  53. package/src/form/field_bindings/MarkdownFieldBinding.tsx +585 -585
  54. package/src/form/validation.ts +5 -1
  55. package/src/internal/EntityView.tsx +6 -6
  56. package/src/internal/useBuildDataSource.ts +0 -2
  57. package/src/internal/useBuildSideEntityController.tsx +27 -15
  58. package/src/preview/components/BooleanPreview.tsx +1 -1
  59. package/src/preview/property_previews/MapPropertyPreview.tsx +3 -1
  60. package/src/preview/property_previews/StringPropertyPreview.tsx +1 -2
  61. package/src/types/collections.ts +2 -2
  62. package/src/types/plugins.tsx +4 -2
  63. package/src/util/enums.ts +1 -2
  64. package/dist/util/chip_utils.d.ts +0 -3
  65. package/src/util/chip_utils.ts +0 -13
@@ -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: TableController<M>;
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 TableController<M extends Record<string, any> = any> = {
424
+ export type EntityTableController<M extends Record<string, any> = any> = {
425
425
  data: Entity<M>[];
426
426
  dataLoading: boolean;
427
427
  noMoreToLoad: boolean;
@@ -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.72",
4
+ "version": "3.0.0-alpha.74",
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.72",
41
+ "@firecms/ui": "^3.0.0-alpha.74",
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": "7f6599121c226abbb4fb4dccbe109b2c8ba6e23e",
110
+ "gitHead": "52ebcaf1e5c019f0f5e03cc63a3ea7663c356d23",
111
111
  "publishConfig": {
112
112
  "access": "public"
113
113
  }
@@ -1,9 +1,9 @@
1
- import { Entity, EntityCallbacks, EntityCollection } from "../../../types";
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 { resolveCollection } from "../../../util";
6
- import { EntityPreview } from "../../EntityPreview";
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 "../../../types";
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 "../../../hooks";
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: boolean,
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 "./internal/PropertyTableCell";
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, getIconForProperty, getResolvedPropertyInPath, getValueInPath } from "../../util";
17
+ import { enumToObjectEntries, getValueInPath } from "../../util";
19
18
  import { getRowHeight } from "../VirtualTable/common";
20
- import { EntityCollectionRowActions } from "./internal/EntityCollectionRowActions";
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 TableController}.
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[] = Object.entries<ResolvedProperty>(properties)
287
- .flatMap(([key, property]) => getColumnKeysForProperty(property, key))
288
- .map(({
289
- key,
290
- disabled
291
- }) => {
292
- const property = getResolvedPropertyInPath(properties, key);
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
- TableController,
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 TableController}
96
+ * {@link EntityTableController}
97
97
  */
98
- tableController: TableController<M>;
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 "../../../types";
11
+ } from "../../types";
12
12
 
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";
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 "../fields/TableStorageUpload";
20
- import { TableReferenceField } from "../fields/TableReferenceField";
19
+ import { TableStorageUpload } from "./fields/TableStorageUpload";
20
+ import { TableReferenceField } from "./fields/TableReferenceField";
21
21
 
22
- import { PropertyPreview } from "../../../preview";
23
- import { getPreviewSizeFrom } from "../../../preview/util";
24
- import { isReadOnly } from "../../../util";
22
+ import { PropertyPreview } from "../../preview";
23
+ import { getPreviewSizeFrom } from "../../preview/util";
24
+ import { isReadOnly } from "../../util";
25
25
 
26
- import { CustomFieldValidator, mapPropertyToYup } from "../../../form/validation";
27
- import { useEntityCollectionTableController } from "../EntityCollectionTable";
28
- import { useClearRestoreValue, useFireCMSContext } from "../../../hooks";
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 { getRowHeight } from "../../VirtualTable/common";
32
- import { EntityTableCellActions } from "./EntityTableCellActions";
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<T>(property: ResolvedProperty) {
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
+ }