@firecms/core 3.0.0-beta.12 → 3.0.0-beta.14
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/app/Scaffold.d.ts +4 -0
- package/dist/components/ArrayContainer.d.ts +13 -11
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
- package/dist/components/EntityJsonPreview.d.ts +3 -0
- package/dist/components/EntityPreview.d.ts +4 -2
- package/dist/components/SelectableTable/SelectableTable.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +0 -4
- package/dist/components/index.d.ts +1 -0
- package/dist/core/DefaultAppBar.d.ts +8 -2
- package/dist/core/EntityEditView.d.ts +8 -1
- package/dist/core/FireCMS.d.ts +2 -2
- package/dist/form/EntityForm.d.ts +5 -1
- package/dist/form/PropertyFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +3 -9
- package/dist/hooks/data/save.d.ts +1 -1
- package/dist/hooks/useBuildNavigationController.d.ts +2 -9
- package/dist/index.es.js +9251 -8721
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +16686 -16160
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +3 -2
- package/dist/internal/useBuildSideEntityController.d.ts +3 -3
- package/dist/types/collections.d.ts +26 -2
- package/dist/types/fields.d.ts +4 -1
- package/dist/types/firecms.d.ts +3 -2
- package/dist/types/navigation.d.ts +9 -0
- package/dist/types/plugins.d.ts +13 -0
- package/dist/types/properties.d.ts +22 -1
- package/dist/types/side_entity_controller.d.ts +4 -0
- package/dist/util/callbacks.d.ts +2 -0
- package/dist/util/createFormexStub.d.ts +2 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/navigation_utils.d.ts +2 -1
- package/dist/util/objects.d.ts +1 -1
- package/dist/util/property_utils.d.ts +2 -2
- package/dist/util/references.d.ts +2 -2
- package/dist/util/resolutions.d.ts +11 -6
- package/package.json +9 -6
- package/src/app/Scaffold.tsx +13 -3
- package/src/components/ArrayContainer.tsx +414 -282
- package/src/components/ClearFilterSortButton.tsx +1 -1
- package/src/components/ConfirmationDialog.tsx +9 -9
- package/src/components/DeleteEntityDialog.tsx +4 -2
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +8 -6
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +13 -2
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +58 -29
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -6
- package/src/components/EntityJsonPreview.tsx +66 -0
- package/src/components/EntityPreview.tsx +27 -16
- package/src/components/EntityView.tsx +4 -1
- package/src/components/ErrorView.tsx +1 -1
- package/src/components/HomePage/DefaultHomePage.tsx +2 -1
- package/src/components/HomePage/NavigationCardBinding.tsx +3 -1
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -1
- package/src/components/SelectableTable/SelectableTable.tsx +140 -143
- package/src/components/VirtualTable/VirtualTable.tsx +37 -31
- package/src/components/VirtualTable/VirtualTableProps.tsx +0 -5
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +0 -1
- package/src/components/common/default_entity_actions.tsx +15 -4
- package/src/components/index.tsx +2 -0
- package/src/core/DefaultAppBar.tsx +17 -5
- package/src/core/EntityEditView.tsx +135 -47
- package/src/core/EntitySidePanel.tsx +15 -20
- package/src/core/FireCMS.tsx +19 -12
- package/src/form/EntityForm.tsx +32 -24
- package/src/form/PropertyFieldBinding.tsx +8 -6
- package/src/form/components/CustomIdField.tsx +3 -1
- package/src/form/components/LabelWithIcon.tsx +1 -1
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +4 -1
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -2
- package/src/form/field_bindings/BlockFieldBinding.tsx +1 -1
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
- package/src/form/field_bindings/MapFieldBinding.tsx +5 -4
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +6 -2
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +3 -3
- package/src/form/field_bindings/RepeatFieldBinding.tsx +9 -2
- package/src/form/field_bindings/SelectFieldBinding.tsx +3 -2
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +227 -156
- package/src/hooks/data/save.ts +24 -35
- package/src/hooks/useBuildNavigationController.tsx +42 -17
- package/src/hooks/useFireCMSContext.tsx +0 -30
- package/src/internal/useBuildDataSource.ts +9 -5
- package/src/internal/useBuildSideEntityController.tsx +26 -20
- package/src/preview/PropertyPreview.tsx +4 -2
- package/src/preview/components/ImagePreview.tsx +2 -2
- package/src/preview/components/ReferencePreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +5 -4
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +4 -3
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +4 -2
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -3
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +4 -2
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +4 -2
- package/src/preview/property_previews/MapPropertyPreview.tsx +1 -1
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +1 -1
- package/src/preview/property_previews/StringPropertyPreview.tsx +1 -1
- package/src/routes/FireCMSRoute.tsx +15 -4
- package/src/types/collections.ts +31 -2
- package/src/types/fields.tsx +5 -1
- package/src/types/firecms.tsx +3 -3
- package/src/types/navigation.ts +11 -0
- package/src/types/plugins.tsx +17 -0
- package/src/types/properties.ts +27 -1
- package/src/types/side_entity_controller.tsx +5 -0
- package/src/util/callbacks.ts +119 -0
- package/src/util/createFormexStub.tsx +62 -0
- package/src/util/index.ts +1 -0
- package/src/util/join_collections.ts +3 -1
- package/src/util/navigation_from_path.ts +5 -1
- package/src/util/navigation_utils.ts +84 -20
- package/src/util/objects.ts +54 -17
- package/src/util/property_utils.tsx +7 -3
- package/src/util/references.ts +8 -6
- package/src/util/resolutions.ts +17 -9
- package/src/util/useStorageUploadController.tsx +21 -2
- package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
- package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
|
@@ -7,13 +7,19 @@ import {
|
|
|
7
7
|
getEntityImagePreviewPropertyKey,
|
|
8
8
|
getEntityPreviewKeys,
|
|
9
9
|
getEntityTitlePropertyKey,
|
|
10
|
+
getPropertyInPath,
|
|
10
11
|
getValueInPath,
|
|
11
12
|
IconForView,
|
|
12
13
|
resolveCollection
|
|
13
14
|
} from "../util";
|
|
14
15
|
import { cls, defaultBorderMixin, IconButton, KeyboardTabIcon, Skeleton, Tooltip, Typography } from "@firecms/ui";
|
|
15
16
|
import { PreviewSize, PropertyPreview, SkeletonPropertyComponent } from "../preview";
|
|
16
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
useAuthController,
|
|
19
|
+
useCustomizationController,
|
|
20
|
+
useNavigationController,
|
|
21
|
+
useSideEntityController
|
|
22
|
+
} from "../hooks";
|
|
17
23
|
import { useAnalyticsController } from "../hooks/useAnalyticsController";
|
|
18
24
|
|
|
19
25
|
export type EntityPreviewProps = {
|
|
@@ -21,11 +27,13 @@ export type EntityPreviewProps = {
|
|
|
21
27
|
actions?: React.ReactNode,
|
|
22
28
|
collection?: EntityCollection,
|
|
23
29
|
hover?: boolean;
|
|
24
|
-
|
|
30
|
+
previewKeys?: string[],
|
|
25
31
|
disabled?: boolean,
|
|
26
32
|
entity: Entity<any>,
|
|
27
33
|
includeId?: boolean,
|
|
34
|
+
includeTitle?: boolean,
|
|
28
35
|
includeEntityLink?: boolean,
|
|
36
|
+
includeImage?: boolean,
|
|
29
37
|
onClick?: (e: React.SyntheticEvent) => void;
|
|
30
38
|
};
|
|
31
39
|
|
|
@@ -38,14 +46,17 @@ export function EntityPreview({
|
|
|
38
46
|
disabled,
|
|
39
47
|
hover,
|
|
40
48
|
collection: collectionProp,
|
|
41
|
-
|
|
49
|
+
previewKeys,
|
|
42
50
|
onClick,
|
|
43
51
|
size,
|
|
44
52
|
includeId = true,
|
|
53
|
+
includeTitle = true,
|
|
45
54
|
includeEntityLink = true,
|
|
46
|
-
|
|
55
|
+
includeImage = true,
|
|
56
|
+
entity,
|
|
47
57
|
}: EntityPreviewProps) {
|
|
48
58
|
|
|
59
|
+
const authController = useAuthController();
|
|
49
60
|
const analyticsController = useAnalyticsController();
|
|
50
61
|
const sideEntityController = useSideEntityController();
|
|
51
62
|
const customizationController = useCustomizationController();
|
|
@@ -62,14 +73,15 @@ export function EntityPreview({
|
|
|
62
73
|
collection,
|
|
63
74
|
path: entity.path,
|
|
64
75
|
values: entity.values,
|
|
65
|
-
propertyConfigs: customizationController.propertyConfigs
|
|
76
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
77
|
+
authController
|
|
66
78
|
}), [collection]);
|
|
67
79
|
|
|
68
|
-
const listProperties = useMemo(() => getEntityPreviewKeys(resolvedCollection, customizationController.propertyConfigs,
|
|
69
|
-
[
|
|
80
|
+
const listProperties = useMemo(() => previewKeys ?? getEntityPreviewKeys(authController, resolvedCollection, customizationController.propertyConfigs, previewKeys, size === "medium" || size === "large" ? 3 : 1),
|
|
81
|
+
[previewKeys, resolvedCollection, size]);
|
|
70
82
|
|
|
71
|
-
const titleProperty = getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs);
|
|
72
|
-
const imagePropertyKey = getEntityImagePreviewPropertyKey(resolvedCollection);
|
|
83
|
+
const titleProperty = includeTitle ? getEntityTitlePropertyKey(resolvedCollection, customizationController.propertyConfigs) : undefined;
|
|
84
|
+
const imagePropertyKey = includeImage ? getEntityImagePreviewPropertyKey(resolvedCollection) : undefined;
|
|
73
85
|
const imageProperty = imagePropertyKey ? resolvedCollection.properties[imagePropertyKey] : undefined;
|
|
74
86
|
const usedImageProperty = imageProperty && "of" in imageProperty ? imageProperty.of : imageProperty;
|
|
75
87
|
const restProperties = listProperties.filter(p => p !== titleProperty && p !== imagePropertyKey);
|
|
@@ -96,9 +108,7 @@ export function EntityPreview({
|
|
|
96
108
|
</div>
|
|
97
109
|
|
|
98
110
|
|
|
99
|
-
<div className={"flex flex-col grow
|
|
100
|
-
"maxWidth": "calc(100% - 96px)"
|
|
101
|
-
}}>
|
|
111
|
+
<div className={"flex flex-col grow w-full m-1 shrink min-w-0"}>
|
|
102
112
|
|
|
103
113
|
{size !== "small" && includeId && (
|
|
104
114
|
entity
|
|
@@ -128,9 +138,10 @@ export function EntityPreview({
|
|
|
128
138
|
)}
|
|
129
139
|
|
|
130
140
|
{restProperties && restProperties.map((key) => {
|
|
131
|
-
const childProperty = resolvedCollection.properties
|
|
141
|
+
const childProperty = getPropertyInPath(resolvedCollection.properties, key);
|
|
132
142
|
if (!childProperty) return null;
|
|
133
143
|
|
|
144
|
+
const valueInPath = getValueInPath(entity.values, key);
|
|
134
145
|
return (
|
|
135
146
|
<div key={"ref_prev_" + key}
|
|
136
147
|
className={cls("truncate", restProperties.length > 1 ? "my-0.5" : "my-0")}>
|
|
@@ -138,7 +149,7 @@ export function EntityPreview({
|
|
|
138
149
|
entity
|
|
139
150
|
? <PropertyPreview
|
|
140
151
|
propertyKey={key as string}
|
|
141
|
-
value={
|
|
152
|
+
value={valueInPath}
|
|
142
153
|
property={childProperty as ResolvedProperty}
|
|
143
154
|
size={"small"}/>
|
|
144
155
|
: <SkeletonPropertyComponent
|
|
@@ -155,7 +166,7 @@ export function EntityPreview({
|
|
|
155
166
|
<Tooltip title={`See details for ${entity.id}`} className={"shrink-0"}>
|
|
156
167
|
<IconButton
|
|
157
168
|
color={"inherit"}
|
|
158
|
-
size={"
|
|
169
|
+
size={"small"}
|
|
159
170
|
className={size !== "small" ? "self-start" : ""}
|
|
160
171
|
onClick={(e) => {
|
|
161
172
|
e.stopPropagation();
|
|
@@ -170,7 +181,7 @@ export function EntityPreview({
|
|
|
170
181
|
updateUrl: true
|
|
171
182
|
});
|
|
172
183
|
}}>
|
|
173
|
-
<KeyboardTabIcon size={"
|
|
184
|
+
<KeyboardTabIcon size={"small"}/>
|
|
174
185
|
</IconButton>
|
|
175
186
|
</Tooltip>}
|
|
176
187
|
|
|
@@ -5,6 +5,7 @@ import { resolveCollection } from "../util";
|
|
|
5
5
|
import { cls, defaultBorderMixin, IconButton, OpenInNewIcon } from "@firecms/ui";
|
|
6
6
|
import { CustomizationController } from "../types/customization_controller";
|
|
7
7
|
import { useCustomizationController } from "../hooks/useCustomizationController";
|
|
8
|
+
import { useAuthController } from "../hooks";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @group Components
|
|
@@ -24,13 +25,15 @@ export function EntityView<M extends Record<string, any>>(
|
|
|
24
25
|
className
|
|
25
26
|
}: EntityViewProps<M>) {
|
|
26
27
|
|
|
28
|
+
const authController = useAuthController();
|
|
27
29
|
const customizationController: CustomizationController = useCustomizationController();
|
|
28
30
|
const resolvedCollection: ResolvedEntityCollection<M> = useMemo(() => resolveCollection<M>({
|
|
29
31
|
collection,
|
|
30
32
|
path,
|
|
31
33
|
entityId: entity.id,
|
|
32
34
|
values: entity.values,
|
|
33
|
-
propertyConfigs: customizationController.propertyConfigs
|
|
35
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
36
|
+
authController
|
|
34
37
|
}), [collection, path, entity, customizationController.propertyConfigs]);
|
|
35
38
|
|
|
36
39
|
const properties: ResolvedProperties = resolvedCollection.properties;
|
|
@@ -114,6 +114,7 @@ export function DefaultHomePage({
|
|
|
114
114
|
);
|
|
115
115
|
})}
|
|
116
116
|
</>;
|
|
117
|
+
|
|
117
118
|
additionalPluginChildrenStart = <div className={"flex flex-col gap-2"}>
|
|
118
119
|
{customizationController.plugins.filter(plugin => plugin.homePage?.additionalChildrenStart)
|
|
119
120
|
.map((plugin, i) => {
|
|
@@ -179,7 +180,7 @@ export function DefaultHomePage({
|
|
|
179
180
|
|
|
180
181
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
181
182
|
{thisGroupCollections.map((entry) => (
|
|
182
|
-
<div key={`nav_${entry.group}_${entry.name}`} className="col-span-1">
|
|
183
|
+
<div key={`nav_${entry.group}_${entry.path}_${entry.name}`} className="col-span-1">
|
|
183
184
|
<NavigationCardBinding
|
|
184
185
|
{...entry}
|
|
185
186
|
onClick={() => {
|
|
@@ -7,6 +7,7 @@ import { useUserConfigurationPersistence } from "../../hooks/useUserConfiguratio
|
|
|
7
7
|
import { IconButton, StarIcon } from "@firecms/ui";
|
|
8
8
|
import { NavigationCard } from "./NavigationCard";
|
|
9
9
|
import { SmallNavigationCard } from "./SmallNavigationCard";
|
|
10
|
+
import React from "react";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* This is the component used in the home page to render a card for each
|
|
@@ -47,6 +48,7 @@ export function NavigationCardBinding({
|
|
|
47
48
|
? [
|
|
48
49
|
<IconButton
|
|
49
50
|
key={"favourite"}
|
|
51
|
+
size={"small"}
|
|
50
52
|
onClick={(e) => {
|
|
51
53
|
e.preventDefault();
|
|
52
54
|
e.stopPropagation();
|
|
@@ -61,7 +63,7 @@ export function NavigationCardBinding({
|
|
|
61
63
|
}
|
|
62
64
|
}}>
|
|
63
65
|
<StarIcon
|
|
64
|
-
size={
|
|
66
|
+
size={"small"}
|
|
65
67
|
className={favourite ? "text-secondary" : "text-surface-400 dark:text-surface-500"}/>
|
|
66
68
|
</IconButton>
|
|
67
69
|
]
|
|
@@ -101,6 +101,7 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
|
|
|
101
101
|
maxSelection,
|
|
102
102
|
}: ReferenceSelectionInnerProps<M>) {
|
|
103
103
|
|
|
104
|
+
const authController = useAuthController();
|
|
104
105
|
const sideDialogContext = useSideDialogContext();
|
|
105
106
|
const sideEntityController = useSideEntityController();
|
|
106
107
|
const navigation = useNavigationController();
|
|
@@ -253,7 +254,8 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
|
|
|
253
254
|
collection: collection,
|
|
254
255
|
path: fullPath,
|
|
255
256
|
values: {},
|
|
256
|
-
propertyConfigs: customizationController.propertyConfigs
|
|
257
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
258
|
+
authController
|
|
257
259
|
}), [collection, customizationController.propertyConfigs, fullPath]);
|
|
258
260
|
|
|
259
261
|
const displayedColumnIds = useColumnIds(resolvedCollection, false);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useRef } from "react";
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
2
|
import {
|
|
3
3
|
CollectionSize,
|
|
4
4
|
Entity,
|
|
@@ -119,150 +119,147 @@ export type SelectableTableProps<M extends Record<string, any>> = {
|
|
|
119
119
|
* @see VirtualTable
|
|
120
120
|
* @group Components
|
|
121
121
|
*/
|
|
122
|
-
export const SelectableTable =
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
122
|
+
export const SelectableTable = function SelectableTable<M extends Record<string, any>>
|
|
123
|
+
({
|
|
124
|
+
onValueChange,
|
|
125
|
+
cellRenderer,
|
|
126
|
+
onEntityClick,
|
|
127
|
+
onColumnResize,
|
|
128
|
+
hoverRow = true,
|
|
129
|
+
size = "m",
|
|
130
|
+
inlineEditing = false,
|
|
131
|
+
tableController:
|
|
132
|
+
{
|
|
133
|
+
data,
|
|
134
|
+
dataLoading,
|
|
135
|
+
noMoreToLoad,
|
|
136
|
+
dataLoadingError,
|
|
137
|
+
filterValues,
|
|
138
|
+
setFilterValues,
|
|
139
|
+
sortBy,
|
|
140
|
+
setSortBy,
|
|
141
|
+
itemCount,
|
|
142
|
+
setItemCount,
|
|
143
|
+
pageSize = 50,
|
|
144
|
+
paginationEnabled,
|
|
145
|
+
checkFilterCombination,
|
|
146
|
+
setPopupCell
|
|
147
|
+
},
|
|
148
|
+
filterable = true,
|
|
149
|
+
onScroll,
|
|
150
|
+
initialScroll,
|
|
151
|
+
emptyComponent,
|
|
152
|
+
columns,
|
|
153
|
+
forceFilter,
|
|
154
|
+
highlightedRow,
|
|
155
|
+
endAdornment,
|
|
156
|
+
AddColumnComponent
|
|
157
|
+
}: SelectableTableProps<M>) {
|
|
158
|
+
|
|
159
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
160
|
+
|
|
161
|
+
const [selectedCell, setSelectedCell] = React.useState<SelectedCellProps<M> | undefined>(undefined);
|
|
162
|
+
|
|
163
|
+
const loadNextPage = () => {
|
|
164
|
+
if (!paginationEnabled || dataLoading || noMoreToLoad)
|
|
165
|
+
return;
|
|
166
|
+
if (itemCount !== undefined)
|
|
167
|
+
setItemCount?.(itemCount + pageSize);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const resetPagination = useCallback(() => {
|
|
171
|
+
setItemCount?.(pageSize);
|
|
172
|
+
}, [pageSize]);
|
|
173
|
+
|
|
174
|
+
const onRowClick = useCallback(({ rowData }: {
|
|
175
|
+
rowData: Entity<M>
|
|
176
|
+
}) => {
|
|
177
|
+
if (inlineEditing)
|
|
178
|
+
return;
|
|
179
|
+
return onEntityClick && onEntityClick(rowData);
|
|
180
|
+
}, [onEntityClick, inlineEditing]);
|
|
181
|
+
|
|
182
|
+
useOutsideAlerter(ref,
|
|
183
|
+
() => {
|
|
184
|
+
if (selectedCell) {
|
|
185
|
+
unselect();
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
Boolean(selectedCell));
|
|
189
|
+
|
|
190
|
+
const select = useCallback((cell?: SelectedCellProps<M>) => {
|
|
191
|
+
setSelectedCell(cell);
|
|
192
|
+
}, []);
|
|
193
|
+
|
|
194
|
+
const unselect = useCallback(() => {
|
|
195
|
+
setSelectedCell(undefined);
|
|
196
|
+
}, []);
|
|
197
|
+
|
|
198
|
+
// on ESC key press
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
const escFunction = (event: any) => {
|
|
201
|
+
if (event.keyCode === 27) {
|
|
202
|
+
unselect();
|
|
203
|
+
}
|
|
169
204
|
};
|
|
205
|
+
document.addEventListener("keydown", escFunction, false);
|
|
206
|
+
return () => {
|
|
207
|
+
document.removeEventListener("keydown", escFunction, false);
|
|
208
|
+
};
|
|
209
|
+
}, [unselect]);
|
|
210
|
+
|
|
211
|
+
const onFilterUpdate = useCallback((updatedFilterValues?: FilterValues<any>) => {
|
|
212
|
+
setFilterValues?.({ ...updatedFilterValues, ...forceFilter } as FilterValues<any>);
|
|
213
|
+
}, [forceFilter]);
|
|
214
|
+
|
|
215
|
+
const contextValue = useMemo(() => ({
|
|
216
|
+
setPopupCell: setPopupCell as ((cell?: SelectedCellProps<M>) => void),
|
|
217
|
+
select,
|
|
218
|
+
onValueChange,
|
|
219
|
+
size: size ?? "m",
|
|
220
|
+
selectedCell
|
|
221
|
+
}), [setPopupCell, select, onValueChange, size, selectedCell]);
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<SelectableTableContext.Provider
|
|
225
|
+
value={contextValue}>
|
|
226
|
+
<div className="h-full w-full flex flex-col bg-white dark:bg-surface-950"
|
|
227
|
+
ref={ref}>
|
|
228
|
+
|
|
229
|
+
<VirtualTable
|
|
230
|
+
data={data}
|
|
231
|
+
columns={columns}
|
|
232
|
+
cellRenderer={cellRenderer}
|
|
233
|
+
onRowClick={inlineEditing ? undefined : (onEntityClick ? onRowClick : undefined)}
|
|
234
|
+
onEndReached={loadNextPage}
|
|
235
|
+
onResetPagination={resetPagination}
|
|
236
|
+
error={dataLoadingError}
|
|
237
|
+
onColumnResize={onColumnResize}
|
|
238
|
+
rowHeight={getRowHeight(size)}
|
|
239
|
+
loading={dataLoading}
|
|
240
|
+
filter={filterValues}
|
|
241
|
+
onFilterUpdate={setFilterValues ? onFilterUpdate : undefined}
|
|
242
|
+
sortBy={sortBy}
|
|
243
|
+
onSortByUpdate={setSortBy as ((sortBy?: [string, "asc" | "desc"]) => void)}
|
|
244
|
+
hoverRow={hoverRow}
|
|
245
|
+
initialScroll={initialScroll}
|
|
246
|
+
onScroll={onScroll}
|
|
247
|
+
checkFilterCombination={checkFilterCombination}
|
|
248
|
+
createFilterField={filterable ? createFilterField : undefined}
|
|
249
|
+
rowClassName={useCallback((entity: Entity<M>) => {
|
|
250
|
+
return highlightedRow?.(entity) ? "bg-surface-100 bg-opacity-75 dark:bg-surface-800 dark:bg-opacity-75" : "";
|
|
251
|
+
}, [highlightedRow])}
|
|
252
|
+
className="flex-grow"
|
|
253
|
+
emptyComponent={emptyComponent}
|
|
254
|
+
endAdornment={endAdornment}
|
|
255
|
+
AddColumnComponent={AddColumnComponent}
|
|
256
|
+
/>
|
|
257
|
+
|
|
258
|
+
</div>
|
|
259
|
+
</SelectableTableContext.Provider>
|
|
260
|
+
);
|
|
170
261
|
|
|
171
|
-
|
|
172
|
-
setItemCount?.(pageSize);
|
|
173
|
-
}, [pageSize]);
|
|
174
|
-
|
|
175
|
-
const onRowClick = useCallback(({ rowData }: {
|
|
176
|
-
rowData: Entity<M>
|
|
177
|
-
}) => {
|
|
178
|
-
if (inlineEditing)
|
|
179
|
-
return;
|
|
180
|
-
return onEntityClick && onEntityClick(rowData);
|
|
181
|
-
}, [onEntityClick, inlineEditing]);
|
|
182
|
-
|
|
183
|
-
useOutsideAlerter(ref,
|
|
184
|
-
() => {
|
|
185
|
-
if (selectedCell) {
|
|
186
|
-
unselect();
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
Boolean(selectedCell));
|
|
190
|
-
|
|
191
|
-
// on ESC key press
|
|
192
|
-
useEffect(() => {
|
|
193
|
-
const escFunction = (event: any) => {
|
|
194
|
-
if (event.keyCode === 27) {
|
|
195
|
-
unselect();
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
document.addEventListener("keydown", escFunction, false);
|
|
199
|
-
return () => {
|
|
200
|
-
document.removeEventListener("keydown", escFunction, false);
|
|
201
|
-
};
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
const select = useCallback((cell?: SelectedCellProps<M>) => {
|
|
205
|
-
setSelectedCell(cell);
|
|
206
|
-
}, []);
|
|
207
|
-
|
|
208
|
-
const unselect = useCallback(() => {
|
|
209
|
-
setSelectedCell(undefined);
|
|
210
|
-
}, []);
|
|
211
|
-
|
|
212
|
-
const onFilterUpdate = useCallback((updatedFilterValues?: FilterValues<any>) => {
|
|
213
|
-
setFilterValues?.({ ...updatedFilterValues, ...forceFilter } as FilterValues<any>);
|
|
214
|
-
}, [forceFilter]);
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
<SelectableTableContext.Provider
|
|
218
|
-
value={{
|
|
219
|
-
setPopupCell: setPopupCell as ((cell?: SelectedCellProps<M>) => void),
|
|
220
|
-
select,
|
|
221
|
-
onValueChange,
|
|
222
|
-
size: size ?? "m",
|
|
223
|
-
selectedCell,
|
|
224
|
-
}}
|
|
225
|
-
>
|
|
226
|
-
<div className="h-full w-full flex flex-col bg-white dark:bg-surface-950"
|
|
227
|
-
ref={ref}>
|
|
228
|
-
|
|
229
|
-
<VirtualTable
|
|
230
|
-
data={data}
|
|
231
|
-
columns={columns}
|
|
232
|
-
cellRenderer={cellRenderer}
|
|
233
|
-
onRowClick={inlineEditing ? undefined : (onEntityClick ? onRowClick : undefined)}
|
|
234
|
-
onEndReached={loadNextPage}
|
|
235
|
-
onResetPagination={resetPagination}
|
|
236
|
-
error={dataLoadingError}
|
|
237
|
-
onColumnResize={onColumnResize}
|
|
238
|
-
rowHeight={getRowHeight(size)}
|
|
239
|
-
loading={dataLoading}
|
|
240
|
-
filter={filterValues}
|
|
241
|
-
onFilterUpdate={setFilterValues ? onFilterUpdate : undefined}
|
|
242
|
-
sortBy={sortBy}
|
|
243
|
-
onSortByUpdate={setSortBy as ((sortBy?: [string, "asc" | "desc"]) => void)}
|
|
244
|
-
hoverRow={hoverRow}
|
|
245
|
-
initialScroll={initialScroll}
|
|
246
|
-
onScroll={onScroll}
|
|
247
|
-
checkFilterCombination={checkFilterCombination}
|
|
248
|
-
createFilterField={filterable ? createFilterField : undefined}
|
|
249
|
-
rowClassName={useCallback((entity: Entity<M>) => {
|
|
250
|
-
return highlightedRow?.(entity) ? "bg-surface-100 bg-opacity-75 dark:bg-surface-800 dark:bg-opacity-75" : "";
|
|
251
|
-
}, [highlightedRow])}
|
|
252
|
-
className="flex-grow"
|
|
253
|
-
emptyComponent={emptyComponent}
|
|
254
|
-
endAdornment={endAdornment}
|
|
255
|
-
AddColumnComponent={AddColumnComponent}
|
|
256
|
-
/>
|
|
257
|
-
|
|
258
|
-
</div>
|
|
259
|
-
</SelectableTableContext.Provider>
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
},
|
|
263
|
-
() => false,
|
|
264
|
-
// equal
|
|
265
|
-
);
|
|
262
|
+
};
|
|
266
263
|
|
|
267
264
|
function createFilterField({
|
|
268
265
|
id,
|
|
@@ -115,7 +115,6 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
115
115
|
endAdornment,
|
|
116
116
|
AddColumnComponent,
|
|
117
117
|
initialScroll = 0,
|
|
118
|
-
debug
|
|
119
118
|
}: VirtualTableProps<T>) {
|
|
120
119
|
|
|
121
120
|
const sortByProperty: string | undefined = sortBy ? sortBy[0] : undefined;
|
|
@@ -131,7 +130,8 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
131
130
|
// Set initial scroll position
|
|
132
131
|
useEffect(() => {
|
|
133
132
|
if (tableRef.current && initialScroll) {
|
|
134
|
-
tableRef.current
|
|
133
|
+
const { scrollLeft } = tableRef.current;
|
|
134
|
+
tableRef.current.scrollTo(scrollLeft, initialScroll);
|
|
135
135
|
}
|
|
136
136
|
}, [tableRef, initialScroll]);
|
|
137
137
|
|
|
@@ -139,17 +139,45 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
139
139
|
setColumns(columnsProp);
|
|
140
140
|
}, [columnsProp]);
|
|
141
141
|
|
|
142
|
-
const [
|
|
142
|
+
const [_, setForceUpdate] = useState(false);
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
// Create a ResizeObserver to detect size changes more aggressively
|
|
145
|
+
if (tableRef.current) {
|
|
146
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
147
|
+
// Force a re-render when size changes
|
|
148
|
+
setForceUpdate(prev => !prev);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
resizeObserver.observe(tableRef.current);
|
|
152
|
+
|
|
153
|
+
return () => {
|
|
154
|
+
if (tableRef.current) {
|
|
155
|
+
resizeObserver.unobserve(tableRef.current);
|
|
156
|
+
}
|
|
157
|
+
resizeObserver.disconnect();
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return () => {
|
|
161
|
+
}
|
|
162
|
+
}, [tableRef]);
|
|
163
|
+
|
|
164
|
+
const [measureRef, bounds] = useMeasure({
|
|
165
|
+
debounce: 50,
|
|
166
|
+
polyfill: ResizeObserver,
|
|
167
|
+
scroll: true,
|
|
168
|
+
// This is important for handling zooming in react-flow
|
|
169
|
+
offsetSize: true
|
|
170
|
+
});
|
|
143
171
|
|
|
144
172
|
const onColumnResizeInternal = useCallback((params: OnVirtualTableColumnResizeParams) => {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
173
|
+
setColumns(prevColumns =>
|
|
174
|
+
prevColumns.map((column) =>
|
|
175
|
+
column.key === params.column.key ? params.column : column
|
|
176
|
+
)
|
|
177
|
+
);
|
|
178
|
+
}, []);
|
|
149
179
|
|
|
150
180
|
const onColumnResizeEndInternal = useCallback((params: OnVirtualTableColumnResizeParams) => {
|
|
151
|
-
if (debug)
|
|
152
|
-
console.log("onColumnResizeEndInternal", params);
|
|
153
181
|
setColumns(columns.map((column) => column.key === params.column.key ? params.column : column));
|
|
154
182
|
if (onColumnResize) {
|
|
155
183
|
onColumnResize(params);
|
|
@@ -160,14 +188,10 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
160
188
|
const filterRef = useRef<VirtualTableFilterValues<any> | undefined>();
|
|
161
189
|
|
|
162
190
|
useEffect(() => {
|
|
163
|
-
if (debug)
|
|
164
|
-
console.log("Filter updated", filterInput);
|
|
165
191
|
filterRef.current = filterInput;
|
|
166
192
|
}, [filterInput]);
|
|
167
193
|
|
|
168
194
|
const scrollToTop = useCallback(() => {
|
|
169
|
-
if (debug)
|
|
170
|
-
console.log("scrollToTop");
|
|
171
195
|
endReachCallbackThreshold.current = 0;
|
|
172
196
|
if (tableRef.current) {
|
|
173
197
|
tableRef.current.scrollTo(tableRef.current?.scrollLeft, 0);
|
|
@@ -176,9 +200,6 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
176
200
|
|
|
177
201
|
const onColumnSort = useCallback((key: string) => {
|
|
178
202
|
|
|
179
|
-
if (debug)
|
|
180
|
-
console.log("onColumnSort", key);
|
|
181
|
-
|
|
182
203
|
const isDesc = sortByProperty === key && currentSort === "desc";
|
|
183
204
|
const isAsc = sortByProperty === key && currentSort === "asc";
|
|
184
205
|
const newSort = isAsc ? "desc" : (isDesc ? undefined : "asc");
|
|
@@ -206,12 +227,8 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
206
227
|
}, [checkFilterCombination, currentSort, onFilterUpdate, onResetPagination, onSortByUpdate, scrollToTop, sortByProperty]);
|
|
207
228
|
|
|
208
229
|
const maxScroll = Math.max((data?.length ?? 0) * rowHeight - bounds.height, 0);
|
|
209
|
-
if (debug)
|
|
210
|
-
console.log("maxScroll", maxScroll);
|
|
211
230
|
|
|
212
231
|
const onEndReachedInternal = useCallback((scrollOffset: number) => {
|
|
213
|
-
if (debug)
|
|
214
|
-
console.log("onEndReachedInternal", scrollOffset, endReachCallbackThreshold.current + endOffset);
|
|
215
232
|
if (onEndReached && (data?.length ?? 0) > 0 && scrollOffset > endReachCallbackThreshold.current + endOffset) {
|
|
216
233
|
endReachCallbackThreshold.current = scrollOffset;
|
|
217
234
|
onEndReached();
|
|
@@ -227,12 +244,6 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
227
244
|
scrollOffset: number,
|
|
228
245
|
scrollUpdateWasRequested: boolean;
|
|
229
246
|
}) => {
|
|
230
|
-
if (debug)
|
|
231
|
-
console.log("onScroll", {
|
|
232
|
-
scrollDirection,
|
|
233
|
-
scrollOffset,
|
|
234
|
-
scrollUpdateWasRequested
|
|
235
|
-
});
|
|
236
247
|
if (onScrollProp) {
|
|
237
248
|
debouncedScroll({
|
|
238
249
|
scrollDirection,
|
|
@@ -245,8 +256,6 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
245
256
|
}, [maxScroll, onEndReachedInternal]);
|
|
246
257
|
|
|
247
258
|
const onFilterUpdateInternal = useCallback((column: VirtualTableColumn, filterForProperty?: [VirtualTableWhereFilterOp, any]) => {
|
|
248
|
-
if (debug)
|
|
249
|
-
console.log("onFilterUpdateInternal", column, filterForProperty);
|
|
250
259
|
|
|
251
260
|
endReachCallbackThreshold.current = 0;
|
|
252
261
|
const filter = filterRef.current;
|
|
@@ -309,9 +318,6 @@ export const VirtualTable = React.memo<VirtualTableProps<any>>(
|
|
|
309
318
|
AddColumnComponent
|
|
310
319
|
};
|
|
311
320
|
|
|
312
|
-
if (debug)
|
|
313
|
-
console.log("VirtualTable render", virtualListController);
|
|
314
|
-
|
|
315
321
|
return (
|
|
316
322
|
<div
|
|
317
323
|
ref={measureRef}
|