@etsoo/react 1.4.18 → 1.4.22
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/lib/components/GridLoader.d.ts +6 -2
- package/lib/components/ScrollerGrid.d.ts +1 -1
- package/lib/components/ScrollerGrid.js +50 -43
- package/lib/components/ScrollerList.js +30 -32
- package/lib/mu/DataGridEx.d.ts +1 -1
- package/lib/mu/DataGridEx.js +23 -49
- package/lib/mu/DataGridRenderers.d.ts +1 -1
- package/lib/mu/DataGridRenderers.js +4 -4
- package/lib/mu/GridMethodRef.d.ts +2 -1
- package/lib/mu/ResponsibleContainer.d.ts +16 -0
- package/lib/mu/ResponsibleContainer.js +94 -40
- package/lib/mu/ScrollerListEx.js +1 -2
- package/lib/mu/TableEx.js +27 -25
- package/lib/mu/pages/CommonPage.d.ts +1 -2
- package/lib/uses/useDimensions.js +0 -8
- package/package.json +8 -8
- package/src/components/GridLoader.ts +7 -2
- package/src/components/ScrollerGrid.tsx +69 -63
- package/src/components/ScrollerList.tsx +44 -48
- package/src/mu/DataGridEx.tsx +51 -91
- package/src/mu/DataGridRenderers.tsx +7 -8
- package/src/mu/GridMethodRef.ts +3 -1
- package/src/mu/ResponsibleContainer.tsx +141 -47
- package/src/mu/ScrollerListEx.tsx +1 -2
- package/src/mu/TableEx.tsx +40 -46
- package/src/uses/useDimensions.ts +0 -11
|
@@ -1,57 +1,104 @@
|
|
|
1
1
|
import { Box, Stack } from '@mui/material';
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { Labels } from '../app/Labels';
|
|
3
4
|
import { GridDataGet } from '../components/GridLoader';
|
|
4
5
|
import useCombinedRefs from '../uses/useCombinedRefs';
|
|
5
6
|
import { useDimensions } from '../uses/useDimensions';
|
|
6
7
|
import { DataGridEx, DataGridExCalColumns } from './DataGridEx';
|
|
8
|
+
import { MUGlobal } from './MUGlobal';
|
|
9
|
+
import { PullToRefreshUI } from './PullToRefreshUI';
|
|
7
10
|
import { ScrollerListEx } from './ScrollerListEx';
|
|
8
11
|
import { SearchBar } from './SearchBar';
|
|
12
|
+
/**
|
|
13
|
+
* Responsible container
|
|
14
|
+
* @param props Props
|
|
15
|
+
* @returns Layout
|
|
16
|
+
*/
|
|
9
17
|
export function ResponsibleContainer(props) {
|
|
10
|
-
var _a;
|
|
11
18
|
// Destruct
|
|
12
|
-
const { adjustHeight, columns, dataGridMinWidth = DataGridExCalColumns(columns).total, fields, fieldTemplate, height, listBoxSx, loadData, mRef, searchBoxSx, sizeReadyMiliseconds = 0, ...rest } = props;
|
|
19
|
+
const { adjustHeight, columns, dataGridMinWidth = DataGridExCalColumns(columns).total, fields, fieldTemplate, height, listBoxSx, loadData, mRef, paddings = MUGlobal.pagePaddings, pullToRefresh = true, searchBoxSx = { marginBottom: MUGlobal.half(paddings) }, sizeReadyMiliseconds = 0, ...rest } = props;
|
|
20
|
+
// Labels
|
|
21
|
+
const labels = Labels.CommonPage;
|
|
13
22
|
// Refs
|
|
14
23
|
const refs = React.useRef({});
|
|
24
|
+
const state = refs.current;
|
|
15
25
|
const mRefs = useCombinedRefs(mRef, (ref) => {
|
|
16
26
|
if (ref == null)
|
|
17
27
|
return;
|
|
18
|
-
|
|
28
|
+
state.ref = ref;
|
|
19
29
|
});
|
|
30
|
+
// Update mounted state
|
|
31
|
+
React.useEffect(() => {
|
|
32
|
+
return () => {
|
|
33
|
+
state.mounted = false;
|
|
34
|
+
};
|
|
35
|
+
}, []);
|
|
20
36
|
// Has fields
|
|
21
37
|
const hasFields = fields != null && fields.length > 0;
|
|
38
|
+
// Load data
|
|
39
|
+
const localLoadData = (props) => {
|
|
40
|
+
state.mounted = true;
|
|
41
|
+
const data = GridDataGet(props, fieldTemplate);
|
|
42
|
+
return loadData(data);
|
|
43
|
+
};
|
|
44
|
+
// On submit callback
|
|
45
|
+
const onSubmit = (data, _reset) => {
|
|
46
|
+
if (data == null || state.ref == null)
|
|
47
|
+
return;
|
|
48
|
+
state.ref.reset({ data });
|
|
49
|
+
};
|
|
22
50
|
// Watch container
|
|
23
|
-
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds)
|
|
51
|
+
const { dimensions } = useDimensions(1, undefined, sizeReadyMiliseconds, (_preRect, rect) => {
|
|
52
|
+
// Check
|
|
53
|
+
if (rect == null)
|
|
54
|
+
return true;
|
|
55
|
+
// Last rect
|
|
56
|
+
const lastRect = state.rect;
|
|
57
|
+
// 32 = scroll bar width
|
|
58
|
+
if (lastRect != null &&
|
|
59
|
+
state.mounted !== true &&
|
|
60
|
+
Math.abs(rect.width - lastRect.width) <= 32 &&
|
|
61
|
+
Math.abs(rect.height - lastRect.height) <= 32)
|
|
62
|
+
return true;
|
|
63
|
+
// Hold the new rect
|
|
64
|
+
state.rect = rect;
|
|
65
|
+
return false;
|
|
66
|
+
});
|
|
67
|
+
// Rect
|
|
24
68
|
const rect = dimensions[0][2];
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
69
|
+
// Create list
|
|
70
|
+
const [list, showDataGrid] = (() => {
|
|
71
|
+
// No layout
|
|
72
|
+
if (rect == null)
|
|
73
|
+
return [null, false];
|
|
74
|
+
// Width
|
|
75
|
+
const width = rect.width;
|
|
76
|
+
// Show DataGrid or List dependng on width
|
|
77
|
+
const showDataGrid = width >= dataGridMinWidth;
|
|
78
|
+
// Height
|
|
79
|
+
let heightLocal;
|
|
80
|
+
if (height != null) {
|
|
81
|
+
heightLocal = height;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Auto calculation
|
|
85
|
+
heightLocal = window.innerHeight - Math.round(rect.bottom + 1);
|
|
29
86
|
const style = window.getComputedStyle(dimensions[0][1]);
|
|
30
|
-
const
|
|
31
|
-
if (!isNaN(
|
|
32
|
-
|
|
87
|
+
const boxMargin = parseFloat(style.marginBottom);
|
|
88
|
+
if (!isNaN(boxMargin))
|
|
89
|
+
heightLocal -= 3 * boxMargin; // 1 - Box, 2 - Page bottom
|
|
33
90
|
if (adjustHeight != null) {
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
if (gridHeight !== refs.current.height) {
|
|
37
|
-
refs.current.height = gridHeight;
|
|
91
|
+
heightLocal -= adjustHeight(heightLocal);
|
|
38
92
|
}
|
|
39
93
|
}
|
|
40
|
-
}, [rect]);
|
|
41
|
-
const localLoadData = (props) => {
|
|
42
|
-
const data = GridDataGet(props, fieldTemplate);
|
|
43
|
-
return loadData(data);
|
|
44
|
-
};
|
|
45
|
-
const gridHeight = refs.current.height;
|
|
46
|
-
console.log(rect, gridHeight, showDataGrid);
|
|
47
|
-
const list = React.useMemo(() => {
|
|
48
|
-
if (gridHeight == null)
|
|
49
|
-
return;
|
|
50
94
|
if (showDataGrid) {
|
|
51
95
|
// Delete
|
|
52
96
|
delete rest.itemRenderer;
|
|
53
|
-
return
|
|
54
|
-
React.createElement(
|
|
97
|
+
return [
|
|
98
|
+
React.createElement(Box, { sx: listBoxSx == null ? undefined : listBoxSx(true) },
|
|
99
|
+
React.createElement(DataGridEx, { autoLoad: !hasFields, height: heightLocal, width: rect.width, loadData: localLoadData, mRef: mRefs, columns: columns, ...rest })),
|
|
100
|
+
true
|
|
101
|
+
];
|
|
55
102
|
}
|
|
56
103
|
// Delete
|
|
57
104
|
delete rest.checkable;
|
|
@@ -62,18 +109,25 @@ export function ResponsibleContainer(props) {
|
|
|
62
109
|
delete rest.hideFooter;
|
|
63
110
|
delete rest.hoverColor;
|
|
64
111
|
delete rest.selectable;
|
|
65
|
-
return
|
|
66
|
-
React.createElement(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
112
|
+
return [
|
|
113
|
+
React.createElement(Box, { sx: listBoxSx == null ? undefined : listBoxSx(false) },
|
|
114
|
+
React.createElement(ScrollerListEx, { autoLoad: !hasFields, width: rect.width, height: heightLocal, loadData: localLoadData, mRef: mRefs, ...rest })),
|
|
115
|
+
false
|
|
116
|
+
];
|
|
117
|
+
})();
|
|
118
|
+
// Pull container
|
|
119
|
+
const pullContainer = list
|
|
120
|
+
? showDataGrid
|
|
121
|
+
? '.DataGridEx-Body'
|
|
122
|
+
: '.ScrollerListEx-Body'
|
|
123
|
+
: undefined;
|
|
74
124
|
// Layout
|
|
75
|
-
return (React.createElement(
|
|
76
|
-
|
|
77
|
-
React.createElement(SearchBar, { fields: fields, onSubmit: onSubmit }))),
|
|
78
|
-
|
|
125
|
+
return (React.createElement(React.Fragment, null,
|
|
126
|
+
React.createElement(Stack, null,
|
|
127
|
+
React.createElement(Box, { ref: dimensions[0][0], sx: searchBoxSx }, hasFields && (React.createElement(SearchBar, { fields: fields, onSubmit: onSubmit }))),
|
|
128
|
+
list),
|
|
129
|
+
pullToRefresh && pullContainer && (React.createElement(PullToRefreshUI, { mainElement: pullContainer, triggerElement: pullContainer, instructionsPullToRefresh: labels.pullToRefresh, instructionsReleaseToRefresh: labels.releaseToRefresh, instructionsRefreshing: labels.refreshing, onRefresh: () => { var _a; return (_a = state.ref) === null || _a === void 0 ? void 0 : _a.reset(); }, shouldPullToRefresh: () => {
|
|
130
|
+
const container = document.querySelector(pullContainer);
|
|
131
|
+
return !(container === null || container === void 0 ? void 0 : container.scrollTop);
|
|
132
|
+
} }))));
|
|
79
133
|
}
|
package/lib/mu/ScrollerListEx.js
CHANGED
|
@@ -66,8 +66,7 @@ export function ScrollerListEx(props) {
|
|
|
66
66
|
const [selectedDiv, selectedData] = (_a = selectedItem.current) !== null && _a !== void 0 ? _a : [];
|
|
67
67
|
if (selectedData != null && selectedData[idField] === data[idField])
|
|
68
68
|
return;
|
|
69
|
-
|
|
70
|
-
selectedDiv.classList.remove(selectedClassName);
|
|
69
|
+
selectedDiv === null || selectedDiv === void 0 ? void 0 : selectedDiv.classList.remove(selectedClassName);
|
|
71
70
|
div.classList.add(selectedClassName);
|
|
72
71
|
selectedItem.current = [div, data];
|
|
73
72
|
if (onSelectChange)
|
package/lib/mu/TableEx.js
CHANGED
|
@@ -34,35 +34,39 @@ export function TableEx(props) {
|
|
|
34
34
|
else {
|
|
35
35
|
rowsPerPageLocal = 10;
|
|
36
36
|
}
|
|
37
|
+
// Rows
|
|
38
|
+
const [rows, updateRows] = React.useState([]);
|
|
39
|
+
const setRows = (rows) => {
|
|
40
|
+
state.loadedItems = rows.length;
|
|
41
|
+
updateRows(rows);
|
|
42
|
+
};
|
|
37
43
|
// States
|
|
38
|
-
const
|
|
39
|
-
return { ...currentState, ...newState };
|
|
40
|
-
}, {
|
|
44
|
+
const stateRefs = React.useRef({
|
|
41
45
|
autoLoad,
|
|
42
46
|
currentPage: 0,
|
|
47
|
+
loadedItems: 0,
|
|
43
48
|
hasNextPage: true,
|
|
44
49
|
isNextPageLoading: false,
|
|
45
50
|
orderBy: defaultOrderBy,
|
|
46
51
|
orderByAsc: defaultOrderBy
|
|
47
52
|
? (_a = columns.find((column) => column.field === defaultOrderBy)) === null || _a === void 0 ? void 0 : _a.sortAsc
|
|
48
53
|
: undefined,
|
|
49
|
-
rows: [],
|
|
50
54
|
batchSize: rowsPerPageLocal,
|
|
51
55
|
selectedItems: []
|
|
52
56
|
});
|
|
53
|
-
const
|
|
57
|
+
const state = stateRefs.current;
|
|
54
58
|
// Reset the state and load again
|
|
55
59
|
const reset = (add) => {
|
|
56
|
-
const
|
|
60
|
+
const resetState = {
|
|
57
61
|
autoLoad: true,
|
|
58
62
|
currentPage: 0,
|
|
63
|
+
loadedItems: 0,
|
|
59
64
|
hasNextPage: true,
|
|
60
65
|
isNextPageLoading: false,
|
|
61
66
|
lastLoadedItems: undefined,
|
|
62
|
-
rows: [],
|
|
63
67
|
...add
|
|
64
68
|
};
|
|
65
|
-
|
|
69
|
+
Object.assign(state, resetState);
|
|
66
70
|
};
|
|
67
71
|
React.useImperativeHandle(mRef, () => ({
|
|
68
72
|
/**
|
|
@@ -84,7 +88,7 @@ export function TableEx(props) {
|
|
|
84
88
|
// Update state
|
|
85
89
|
state.isNextPageLoading = true;
|
|
86
90
|
// Parameters
|
|
87
|
-
const { currentPage, batchSize, orderBy, orderByAsc, data } = state;
|
|
91
|
+
const { currentPage, batchSize, orderBy, orderByAsc, data, isMounted } = state;
|
|
88
92
|
const loadProps = {
|
|
89
93
|
currentPage,
|
|
90
94
|
batchSize,
|
|
@@ -93,16 +97,16 @@ export function TableEx(props) {
|
|
|
93
97
|
data
|
|
94
98
|
};
|
|
95
99
|
loadData(loadProps).then((result) => {
|
|
96
|
-
|
|
100
|
+
state.isMounted = true;
|
|
101
|
+
if (result == null || isMounted === false) {
|
|
97
102
|
return;
|
|
98
103
|
}
|
|
99
104
|
const newItems = result.length;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
105
|
+
state.lastLoadedItems = newItems;
|
|
106
|
+
state.hasNextPage = newItems >= batchSize;
|
|
107
|
+
state.isNextPageLoading = false;
|
|
108
|
+
// Update rows
|
|
109
|
+
setRows(result);
|
|
106
110
|
});
|
|
107
111
|
};
|
|
108
112
|
const handleChangePage = (_event, newPage) => {
|
|
@@ -111,11 +115,11 @@ export function TableEx(props) {
|
|
|
111
115
|
loadDataLocal();
|
|
112
116
|
};
|
|
113
117
|
const handleChangeRowsPerPage = (event) => {
|
|
114
|
-
const
|
|
115
|
-
reset({
|
|
118
|
+
const batchSize = parseInt(event.target.value);
|
|
119
|
+
reset({ batchSize });
|
|
116
120
|
};
|
|
117
121
|
const handleSelect = (item, checked) => {
|
|
118
|
-
const selectedItems =
|
|
122
|
+
const selectedItems = state.selectedItems;
|
|
119
123
|
const index = selectedItems.findIndex((selectedItem) => selectedItem[idField] === item[idField]);
|
|
120
124
|
if (checked) {
|
|
121
125
|
if (index === -1)
|
|
@@ -128,11 +132,10 @@ export function TableEx(props) {
|
|
|
128
132
|
if (onSelectChange != null) {
|
|
129
133
|
onSelectChange(selectedItems);
|
|
130
134
|
}
|
|
131
|
-
stateUpdate({ selectedItems });
|
|
132
135
|
};
|
|
133
136
|
const handleSelectAll = (checked) => {
|
|
134
|
-
const selectedItems =
|
|
135
|
-
|
|
137
|
+
const selectedItems = state.selectedItems;
|
|
138
|
+
rows.forEach((row) => {
|
|
136
139
|
const index = selectedItems.findIndex((selectedItem) => selectedItem[idField] === row[idField]);
|
|
137
140
|
if (checked) {
|
|
138
141
|
if (index === -1)
|
|
@@ -145,14 +148,13 @@ export function TableEx(props) {
|
|
|
145
148
|
if (onSelectChange != null) {
|
|
146
149
|
onSelectChange(selectedItems);
|
|
147
150
|
}
|
|
148
|
-
stateUpdate({ selectedItems });
|
|
149
151
|
};
|
|
150
152
|
// New sort
|
|
151
153
|
const handleSort = (field, asc) => {
|
|
152
154
|
reset({ orderBy: field, orderByAsc: asc });
|
|
153
155
|
};
|
|
154
156
|
// Destruct states
|
|
155
|
-
const { autoLoad: stateAutoLoad, currentPage, hasNextPage, lastLoadedItems, orderBy,
|
|
157
|
+
const { autoLoad: stateAutoLoad, currentPage, hasNextPage, lastLoadedItems, orderBy, batchSize, selectedItems } = state;
|
|
156
158
|
// Current page selected items
|
|
157
159
|
const pageSelectedItems = selectable
|
|
158
160
|
? rows.reduce((previousValue, currentItem) => {
|
|
@@ -170,7 +172,7 @@ export function TableEx(props) {
|
|
|
170
172
|
loadDataLocal();
|
|
171
173
|
React.useEffect(() => {
|
|
172
174
|
return () => {
|
|
173
|
-
isMounted
|
|
175
|
+
state.isMounted = false;
|
|
174
176
|
};
|
|
175
177
|
}, []);
|
|
176
178
|
// Layout
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
/// <reference types="react" />
|
|
3
2
|
import { CommonPageProps } from './CommonPageProps';
|
|
4
3
|
/**
|
|
@@ -8,7 +7,7 @@ export declare const CommonPagePullContainer = "#page-container";
|
|
|
8
7
|
/**
|
|
9
8
|
* Default scroll container
|
|
10
9
|
*/
|
|
11
|
-
export declare const CommonPageScrollContainer:
|
|
10
|
+
export declare const CommonPageScrollContainer: typeof globalThis;
|
|
12
11
|
/**
|
|
13
12
|
* Common page
|
|
14
13
|
* @param props Props
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { DomUtils } from '@etsoo/shared';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
const createRef = (source, index) => {
|
|
4
|
-
return [
|
|
5
|
-
(instance) => {
|
|
6
|
-
if (instance != null)
|
|
7
|
-
source[index][1] = instance;
|
|
8
|
-
}
|
|
9
|
-
];
|
|
10
|
-
};
|
|
11
3
|
/**
|
|
12
4
|
* Calculate element(s) dimensions
|
|
13
5
|
* @param elements Observed elments count
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/react",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.22",
|
|
4
4
|
"description": "TypeScript ReactJs framework",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@etsoo/notificationbase": "^1.1.0",
|
|
55
55
|
"@etsoo/shared": "^1.1.3",
|
|
56
56
|
"@mui/icons-material": "^5.2.5",
|
|
57
|
-
"@mui/material": "^5.2.
|
|
57
|
+
"@mui/material": "^5.2.8",
|
|
58
58
|
"@reach/router": "^1.3.4",
|
|
59
59
|
"@types/pica": "^5.1.3",
|
|
60
60
|
"@types/pulltorefreshjs": "^0.1.5",
|
|
@@ -76,15 +76,15 @@
|
|
|
76
76
|
"react-window": "^1.8.6"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@babel/cli": "^7.16.
|
|
79
|
+
"@babel/cli": "^7.16.8",
|
|
80
80
|
"@babel/core": "^7.16.7",
|
|
81
|
-
"@babel/plugin-transform-runtime": "^7.16.
|
|
82
|
-
"@babel/preset-env": "^7.16.
|
|
83
|
-
"@babel/runtime-corejs3": "^7.16.
|
|
81
|
+
"@babel/plugin-transform-runtime": "^7.16.8",
|
|
82
|
+
"@babel/preset-env": "^7.16.8",
|
|
83
|
+
"@babel/runtime-corejs3": "^7.16.8",
|
|
84
84
|
"@types/jest": "^27.4.0",
|
|
85
85
|
"@types/react-test-renderer": "^17.0.1",
|
|
86
|
-
"@typescript-eslint/eslint-plugin": "^5.9.
|
|
87
|
-
"@typescript-eslint/parser": "^5.9.
|
|
86
|
+
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
|
87
|
+
"@typescript-eslint/parser": "^5.9.1",
|
|
88
88
|
"eslint": "^8.6.0",
|
|
89
89
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
90
90
|
"eslint-plugin-import": "^2.25.4",
|
|
@@ -126,6 +126,11 @@ export interface GridLoaderStates<T> extends GridLoadDataProps {
|
|
|
126
126
|
*/
|
|
127
127
|
lastLoadedItems?: number;
|
|
128
128
|
|
|
129
|
+
/**
|
|
130
|
+
* All loaded items count
|
|
131
|
+
*/
|
|
132
|
+
loadedItems: number;
|
|
133
|
+
|
|
129
134
|
/**
|
|
130
135
|
* Has next page?
|
|
131
136
|
*/
|
|
@@ -137,9 +142,9 @@ export interface GridLoaderStates<T> extends GridLoadDataProps {
|
|
|
137
142
|
isNextPageLoading: boolean;
|
|
138
143
|
|
|
139
144
|
/**
|
|
140
|
-
*
|
|
145
|
+
* Is mounted
|
|
141
146
|
*/
|
|
142
|
-
|
|
147
|
+
isMounted?: boolean;
|
|
143
148
|
|
|
144
149
|
/**
|
|
145
150
|
* Selected items of id
|
|
@@ -40,7 +40,10 @@ export interface ScrollerGridProps<T>
|
|
|
40
40
|
/**
|
|
41
41
|
* Footer renderer
|
|
42
42
|
*/
|
|
43
|
-
footerRenderer?: (
|
|
43
|
+
footerRenderer?: (
|
|
44
|
+
rows: T[],
|
|
45
|
+
states: GridLoaderStates<T>
|
|
46
|
+
) => React.ReactNode;
|
|
44
47
|
|
|
45
48
|
/**
|
|
46
49
|
* Header renderer
|
|
@@ -163,29 +166,28 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
163
166
|
...rest
|
|
164
167
|
} = props;
|
|
165
168
|
|
|
166
|
-
//
|
|
167
|
-
const [
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return { ...currentState, ...newState };
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
autoLoad,
|
|
176
|
-
currentPage: 0,
|
|
177
|
-
hasNextPage: true,
|
|
178
|
-
isNextPageLoading: false,
|
|
179
|
-
orderBy: defaultOrderBy,
|
|
180
|
-
orderByAsc: defaultOrderByAsc,
|
|
181
|
-
rows: [],
|
|
182
|
-
batchSize: 10,
|
|
183
|
-
selectedItems: []
|
|
184
|
-
}
|
|
185
|
-
);
|
|
186
|
-
const isMounted = React.useRef(true);
|
|
169
|
+
// Rows
|
|
170
|
+
const [rows, updateRows] = React.useState<T[]>([]);
|
|
171
|
+
const setRows = (rows: T[]) => {
|
|
172
|
+
state.loadedItems = rows.length;
|
|
173
|
+
updateRows(rows);
|
|
174
|
+
};
|
|
187
175
|
|
|
188
|
-
|
|
176
|
+
// Refs
|
|
177
|
+
const refs = React.useRef<GridLoaderStates<T>>({
|
|
178
|
+
autoLoad,
|
|
179
|
+
currentPage: 0,
|
|
180
|
+
hasNextPage: true,
|
|
181
|
+
isNextPageLoading: false,
|
|
182
|
+
orderBy: defaultOrderBy,
|
|
183
|
+
orderByAsc: defaultOrderByAsc,
|
|
184
|
+
batchSize: 10,
|
|
185
|
+
loadedItems: 0,
|
|
186
|
+
selectedItems: []
|
|
187
|
+
});
|
|
188
|
+
const state = refs.current;
|
|
189
|
+
|
|
190
|
+
const ref = React.useRef<VariableSizeGrid<T>>(null);
|
|
189
191
|
|
|
190
192
|
// Load data
|
|
191
193
|
const loadDataLocal = (pageAdd: number = 1) => {
|
|
@@ -194,10 +196,10 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
194
196
|
|
|
195
197
|
// Update state
|
|
196
198
|
state.isNextPageLoading = true;
|
|
197
|
-
// stateUpdate({ isNextPageLoading: true });
|
|
198
199
|
|
|
199
200
|
// Parameters
|
|
200
|
-
const { currentPage, batchSize, orderBy, orderByAsc, data } =
|
|
201
|
+
const { currentPage, batchSize, orderBy, orderByAsc, data, isMounted } =
|
|
202
|
+
state;
|
|
201
203
|
|
|
202
204
|
const loadProps: GridLoadDataProps = {
|
|
203
205
|
currentPage,
|
|
@@ -208,38 +210,36 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
208
210
|
};
|
|
209
211
|
|
|
210
212
|
loadData(loadProps).then((result) => {
|
|
211
|
-
|
|
213
|
+
state.isMounted = true;
|
|
214
|
+
|
|
215
|
+
if (result == null || isMounted === false) {
|
|
212
216
|
return;
|
|
213
217
|
}
|
|
214
218
|
|
|
215
219
|
const newItems = result.length;
|
|
220
|
+
state.lastLoadedItems = newItems;
|
|
221
|
+
state.isNextPageLoading = false;
|
|
222
|
+
state.hasNextPage = newItems >= batchSize;
|
|
216
223
|
|
|
217
224
|
if (pageAdd === 0) {
|
|
218
225
|
// New items
|
|
219
|
-
const
|
|
220
|
-
?
|
|
226
|
+
const newRows = state.lastLoadedItems
|
|
227
|
+
? [...rows]
|
|
221
228
|
.splice(
|
|
222
|
-
|
|
229
|
+
rows.length - state.lastLoadedItems,
|
|
223
230
|
state.lastLoadedItems
|
|
224
231
|
)
|
|
225
232
|
.concat(result)
|
|
226
233
|
: result;
|
|
227
234
|
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
rows,
|
|
231
|
-
lastLoadedItems: newItems,
|
|
232
|
-
isNextPageLoading: false,
|
|
233
|
-
hasNextPage: newItems >= batchSize
|
|
234
|
-
});
|
|
235
|
+
// Update rows
|
|
236
|
+
setRows(newRows);
|
|
235
237
|
} else {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
hasNextPage: newItems >= batchSize
|
|
242
|
-
});
|
|
238
|
+
// Set current page
|
|
239
|
+
state.currentPage = state.currentPage + pageAdd;
|
|
240
|
+
|
|
241
|
+
// Update rows
|
|
242
|
+
setRows([...rows, ...result]);
|
|
243
243
|
}
|
|
244
244
|
});
|
|
245
245
|
};
|
|
@@ -251,8 +251,8 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
251
251
|
) => {
|
|
252
252
|
// Custom render
|
|
253
253
|
const data =
|
|
254
|
-
itemProps.rowIndex <
|
|
255
|
-
?
|
|
254
|
+
itemProps.rowIndex < rows.length
|
|
255
|
+
? rows[itemProps.rowIndex]
|
|
256
256
|
: undefined;
|
|
257
257
|
return itemRenderer({
|
|
258
258
|
...itemProps,
|
|
@@ -264,7 +264,7 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
264
264
|
// Local items renderer callback
|
|
265
265
|
const onItemsRenderedLocal = (props: GridOnItemsRenderedProps) => {
|
|
266
266
|
// No items, means no necessary to load more data during reset
|
|
267
|
-
const itemCount =
|
|
267
|
+
const itemCount = rows.length;
|
|
268
268
|
if (
|
|
269
269
|
itemCount > 0 &&
|
|
270
270
|
props.visibleRowStopIndex + threshold > itemCount
|
|
@@ -278,17 +278,20 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
278
278
|
};
|
|
279
279
|
|
|
280
280
|
// Reset the state and load again
|
|
281
|
-
const reset = (add?:
|
|
282
|
-
const
|
|
281
|
+
const reset = (add?: Partial<GridLoaderStates<T>>) => {
|
|
282
|
+
const resetState: Partial<GridLoaderStates<T>> = {
|
|
283
283
|
autoLoad: true,
|
|
284
284
|
currentPage: 0,
|
|
285
|
+
loadedItems: 0,
|
|
285
286
|
hasNextPage: true,
|
|
286
287
|
isNextPageLoading: false,
|
|
287
288
|
lastLoadedItems: undefined,
|
|
288
|
-
rows: [],
|
|
289
289
|
...add
|
|
290
290
|
};
|
|
291
|
-
|
|
291
|
+
Object.assign(state, resetState);
|
|
292
|
+
|
|
293
|
+
// Reset items
|
|
294
|
+
setRows([]);
|
|
292
295
|
};
|
|
293
296
|
|
|
294
297
|
React.useImperativeHandle(
|
|
@@ -307,16 +310,14 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
307
310
|
select(rowIndex: number) {
|
|
308
311
|
// Select only one item
|
|
309
312
|
const selectedItems = state.selectedItems;
|
|
310
|
-
selectedItems[0] =
|
|
313
|
+
selectedItems[0] = rows[rowIndex];
|
|
311
314
|
|
|
312
315
|
if (onSelectChange) onSelectChange(selectedItems);
|
|
313
|
-
|
|
314
|
-
stateUpdate({ selectedItems });
|
|
315
316
|
},
|
|
316
317
|
selectAll(checked: boolean) {
|
|
317
318
|
const selectedItems = state.selectedItems;
|
|
318
319
|
|
|
319
|
-
|
|
320
|
+
rows.forEach((row) => {
|
|
320
321
|
const index = selectedItems.findIndex(
|
|
321
322
|
(selectedItem) => selectedItem[idField] === row[idField]
|
|
322
323
|
);
|
|
@@ -329,8 +330,6 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
329
330
|
});
|
|
330
331
|
|
|
331
332
|
if (onSelectChange) onSelectChange(selectedItems);
|
|
332
|
-
|
|
333
|
-
stateUpdate({ selectedItems });
|
|
334
333
|
},
|
|
335
334
|
selectItem(item: T, checked: boolean) {
|
|
336
335
|
const selectedItems = state.selectedItems;
|
|
@@ -345,8 +344,6 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
345
344
|
}
|
|
346
345
|
|
|
347
346
|
if (onSelectChange) onSelectChange(selectedItems);
|
|
348
|
-
|
|
349
|
-
stateUpdate({ selectedItems });
|
|
350
347
|
},
|
|
351
348
|
reset,
|
|
352
349
|
resetAfterColumnIndex(index: number, shouldForceUpdate?: boolean) {
|
|
@@ -363,17 +360,26 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
363
360
|
ref.current?.resetAfterRowIndex(index, shouldForceUpdate);
|
|
364
361
|
}
|
|
365
362
|
}),
|
|
366
|
-
[
|
|
363
|
+
[rows]
|
|
367
364
|
);
|
|
368
365
|
|
|
369
366
|
React.useEffect(() => {
|
|
370
367
|
return () => {
|
|
371
|
-
isMounted
|
|
368
|
+
state.isMounted = false;
|
|
372
369
|
};
|
|
373
370
|
}, []);
|
|
374
371
|
|
|
372
|
+
// Force update to work with the new width
|
|
373
|
+
React.useEffect(() => {
|
|
374
|
+
ref.current?.resetAfterIndices({
|
|
375
|
+
columnIndex: 0,
|
|
376
|
+
rowIndex: 0,
|
|
377
|
+
shouldForceUpdate: true
|
|
378
|
+
});
|
|
379
|
+
}, [width]);
|
|
380
|
+
|
|
375
381
|
// Destruct state
|
|
376
|
-
const { autoLoad: stateAutoLoad,
|
|
382
|
+
const { autoLoad: stateAutoLoad, hasNextPage, currentPage } = state;
|
|
377
383
|
const rowLength = rows.length;
|
|
378
384
|
|
|
379
385
|
// Row count
|
|
@@ -388,7 +394,7 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
388
394
|
{headerRenderer && headerRenderer(state)}
|
|
389
395
|
<VariableSizeGrid<T>
|
|
390
396
|
itemKey={({ columnIndex, rowIndex }) => {
|
|
391
|
-
const data =
|
|
397
|
+
const data = rows[rowIndex];
|
|
392
398
|
if (data == null) return [rowIndex, columnIndex].join(',');
|
|
393
399
|
return [data[idField], columnIndex].join(',');
|
|
394
400
|
}}
|
|
@@ -406,7 +412,7 @@ export const ScrollerGrid = <T extends Record<string, unknown>>(
|
|
|
406
412
|
>
|
|
407
413
|
{(props) => itemRendererLocal(props, state)}
|
|
408
414
|
</VariableSizeGrid>
|
|
409
|
-
{footerRenderer && footerRenderer(state)}
|
|
415
|
+
{footerRenderer && footerRenderer(rows, state)}
|
|
410
416
|
</React.Fragment>
|
|
411
417
|
);
|
|
412
418
|
};
|