@etsoo/react 1.8.51 → 1.8.53
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/__tests__/ScrollerGrid.tsx +95 -0
- package/__tests__/ScrollerList.tsx +85 -0
- package/lib/cjs/components/GridLoader.d.ts +2 -2
- package/lib/cjs/components/GridMethodRef.d.ts +12 -7
- package/lib/cjs/components/ScrollerGrid.d.ts +26 -51
- package/lib/cjs/components/ScrollerGrid.js +67 -101
- package/lib/cjs/components/ScrollerList.d.ts +16 -41
- package/lib/cjs/components/ScrollerList.js +33 -52
- package/lib/cjs/index.d.ts +0 -1
- package/lib/mjs/components/GridLoader.d.ts +2 -2
- package/lib/mjs/components/GridMethodRef.d.ts +12 -7
- package/lib/mjs/components/ScrollerGrid.d.ts +26 -51
- package/lib/mjs/components/ScrollerGrid.js +68 -102
- package/lib/mjs/components/ScrollerList.d.ts +16 -41
- package/lib/mjs/components/ScrollerList.js +35 -54
- package/lib/mjs/index.d.ts +0 -1
- package/package.json +14 -14
- package/setupTests.ts +13 -0
- package/src/components/GridLoader.ts +2 -2
- package/src/components/GridMethodRef.ts +14 -8
- package/src/components/ScrollerGrid.tsx +163 -251
- package/src/components/ScrollerList.tsx +71 -152
- package/src/index.ts +0 -5
- package/vite.config.mts +1 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { Grid, useGridRef } from "react-window";
|
|
4
|
+
import { useCombinedRefs } from "../uses/useCombinedRefs";
|
|
4
5
|
/**
|
|
5
6
|
* Scroller vertical grid
|
|
6
7
|
* @param props Props
|
|
@@ -8,17 +9,26 @@ import { VariableSizeGrid } from "react-window";
|
|
|
8
9
|
*/
|
|
9
10
|
export const ScrollerGrid = (props) => {
|
|
10
11
|
// Destruct
|
|
11
|
-
const { autoLoad = true, defaultOrderBy, footerRenderer, headerRenderer,
|
|
12
|
+
const { autoLoad = true, defaultOrderBy, footerRenderer, headerRenderer, height = "100%", gridRef, width = "100%", style = {}, idField = "id", loadBatchSize, loadData, mRef, onCellsRendered, onSelectChange, rowHeight = 53, threshold = 3, onInitLoad, onUpdateRows, ...rest } = props;
|
|
13
|
+
// Style
|
|
14
|
+
Object.assign(style, {
|
|
15
|
+
width,
|
|
16
|
+
height,
|
|
17
|
+
overflowX: "hidden"
|
|
18
|
+
});
|
|
19
|
+
// Refs
|
|
20
|
+
const localRef = useGridRef(null);
|
|
21
|
+
const refs = useCombinedRefs(gridRef, localRef);
|
|
12
22
|
// Rows
|
|
13
23
|
const [rows, updateRows] = React.useState([]);
|
|
14
24
|
const setRows = (rows, reset = false) => {
|
|
15
|
-
|
|
25
|
+
stateRefs.current.loadedItems = rows.length;
|
|
16
26
|
updateRows(rows);
|
|
17
27
|
if (!reset && onUpdateRows)
|
|
18
|
-
onUpdateRows(rows,
|
|
28
|
+
onUpdateRows(rows, stateRefs.current);
|
|
19
29
|
};
|
|
20
|
-
// Refs
|
|
21
|
-
const
|
|
30
|
+
// State Refs
|
|
31
|
+
const stateRefs = React.useRef({
|
|
22
32
|
queryPaging: {
|
|
23
33
|
currentPage: 0,
|
|
24
34
|
orderBy: defaultOrderBy,
|
|
@@ -31,58 +41,58 @@ export const ScrollerGrid = (props) => {
|
|
|
31
41
|
selectedItems: [],
|
|
32
42
|
idCache: {}
|
|
33
43
|
});
|
|
34
|
-
const ref = React.useRef(null);
|
|
35
44
|
// Load data
|
|
36
45
|
const loadDataLocal = (pageAdd = 1) => {
|
|
37
46
|
// Prevent multiple loadings
|
|
38
|
-
if (!
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
if (!stateRefs.current.hasNextPage ||
|
|
48
|
+
stateRefs.current.isNextPageLoading ||
|
|
49
|
+
stateRefs.current.isMounted === false)
|
|
41
50
|
return;
|
|
42
51
|
// Update state
|
|
43
|
-
|
|
52
|
+
stateRefs.current.isNextPageLoading = true;
|
|
44
53
|
// Parameters
|
|
45
|
-
const { queryPaging, data } =
|
|
54
|
+
const { queryPaging, data } = stateRefs.current;
|
|
46
55
|
const loadProps = {
|
|
47
56
|
queryPaging,
|
|
48
57
|
data
|
|
49
58
|
};
|
|
50
|
-
loadData(loadProps,
|
|
51
|
-
if (result == null ||
|
|
59
|
+
loadData(loadProps, stateRefs.current.lastItem).then((result) => {
|
|
60
|
+
if (result == null || stateRefs.current.isMounted === false) {
|
|
52
61
|
return;
|
|
53
62
|
}
|
|
54
|
-
|
|
63
|
+
stateRefs.current.isMounted = true;
|
|
55
64
|
const newItems = result.length;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
stateRefs.current.lastLoadedItems = newItems;
|
|
66
|
+
stateRefs.current.lastItem = result.at(-1);
|
|
67
|
+
stateRefs.current.isNextPageLoading = false;
|
|
68
|
+
stateRefs.current.hasNextPage =
|
|
69
|
+
newItems >= stateRefs.current.queryPaging.batchSize;
|
|
60
70
|
if (pageAdd === 0) {
|
|
61
71
|
// New items
|
|
62
|
-
const newRows =
|
|
72
|
+
const newRows = stateRefs.current.lastLoadedItems
|
|
63
73
|
? [...rows]
|
|
64
|
-
.splice(rows.length -
|
|
74
|
+
.splice(rows.length - stateRefs.current.lastLoadedItems, stateRefs.current.lastLoadedItems)
|
|
65
75
|
.concat(result)
|
|
66
76
|
: result;
|
|
67
|
-
|
|
77
|
+
stateRefs.current.idCache = {};
|
|
68
78
|
for (const row of newRows) {
|
|
69
79
|
const id = row[idField];
|
|
70
|
-
|
|
80
|
+
stateRefs.current.idCache[id] = null;
|
|
71
81
|
}
|
|
72
82
|
// Update rows
|
|
73
83
|
setRows(newRows);
|
|
74
84
|
}
|
|
75
85
|
else {
|
|
76
86
|
// Set current page
|
|
77
|
-
if (
|
|
78
|
-
|
|
87
|
+
if (stateRefs.current.queryPaging.currentPage == null)
|
|
88
|
+
stateRefs.current.queryPaging.currentPage = pageAdd;
|
|
79
89
|
else
|
|
80
|
-
|
|
90
|
+
stateRefs.current.queryPaging.currentPage += pageAdd;
|
|
81
91
|
// Update rows, avoid duplicate items
|
|
82
92
|
const newRows = [...rows];
|
|
83
93
|
for (const item of result) {
|
|
84
94
|
const id = item[idField];
|
|
85
|
-
if (
|
|
95
|
+
if (stateRefs.current.idCache[id] === undefined) {
|
|
86
96
|
newRows.push(item);
|
|
87
97
|
}
|
|
88
98
|
}
|
|
@@ -90,34 +100,6 @@ export const ScrollerGrid = (props) => {
|
|
|
90
100
|
}
|
|
91
101
|
});
|
|
92
102
|
};
|
|
93
|
-
// Item renderer
|
|
94
|
-
const itemRendererLocal = (itemProps, state) => {
|
|
95
|
-
// Custom render
|
|
96
|
-
const data = itemProps.rowIndex < rows.length ? rows[itemProps.rowIndex] : undefined;
|
|
97
|
-
return itemRenderer({
|
|
98
|
-
...itemProps,
|
|
99
|
-
data,
|
|
100
|
-
selectedItems: state.selectedItems,
|
|
101
|
-
setItems: (callback) => {
|
|
102
|
-
const result = callback(rows, instance);
|
|
103
|
-
if (result == null)
|
|
104
|
-
return;
|
|
105
|
-
setRows(result);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
};
|
|
109
|
-
// Local items renderer callback
|
|
110
|
-
const onItemsRenderedLocal = (props) => {
|
|
111
|
-
// No items, means no necessary to load more data during reset
|
|
112
|
-
const itemCount = rows.length;
|
|
113
|
-
if (itemCount > 0 && props.visibleRowStopIndex + threshold > itemCount) {
|
|
114
|
-
// Auto load next page
|
|
115
|
-
loadDataLocal();
|
|
116
|
-
}
|
|
117
|
-
// Custom
|
|
118
|
-
if (onItemsRendered)
|
|
119
|
-
onItemsRendered(props);
|
|
120
|
-
};
|
|
121
103
|
// Reset the state and load again
|
|
122
104
|
const reset = (add, items = []) => {
|
|
123
105
|
const { queryPaging, ...rest } = add ?? {};
|
|
@@ -130,16 +112,16 @@ export const ScrollerGrid = (props) => {
|
|
|
130
112
|
lastItem: undefined,
|
|
131
113
|
...rest
|
|
132
114
|
};
|
|
133
|
-
Object.assign(
|
|
134
|
-
Object.assign(
|
|
115
|
+
Object.assign(stateRefs.current, resetState);
|
|
116
|
+
Object.assign(stateRefs.current.queryPaging, {
|
|
135
117
|
currentPage: 0,
|
|
136
118
|
...queryPaging
|
|
137
119
|
});
|
|
138
120
|
// Reset items
|
|
139
|
-
if (
|
|
121
|
+
if (stateRefs.current.isMounted !== false)
|
|
140
122
|
setRows(items, true);
|
|
141
123
|
};
|
|
142
|
-
|
|
124
|
+
React.useImperativeHandle(mRef, () => ({
|
|
143
125
|
delete(index) {
|
|
144
126
|
const item = rows.at(index);
|
|
145
127
|
if (item) {
|
|
@@ -154,27 +136,28 @@ export const ScrollerGrid = (props) => {
|
|
|
154
136
|
newRows.splice(start, 0, item);
|
|
155
137
|
setRows(newRows);
|
|
156
138
|
},
|
|
157
|
-
|
|
158
|
-
|
|
139
|
+
refresh() {
|
|
140
|
+
loadDataLocal(0);
|
|
159
141
|
},
|
|
160
|
-
|
|
161
|
-
|
|
142
|
+
reset,
|
|
143
|
+
scrollToCell(param) {
|
|
144
|
+
localRef.current?.scrollToCell(param);
|
|
162
145
|
},
|
|
163
|
-
|
|
164
|
-
|
|
146
|
+
scrollToColumn(param) {
|
|
147
|
+
localRef.current?.scrollToColumn(param);
|
|
165
148
|
},
|
|
166
|
-
|
|
167
|
-
|
|
149
|
+
scrollToRow(param) {
|
|
150
|
+
localRef.current?.scrollToRow(param);
|
|
168
151
|
},
|
|
169
152
|
select(rowIndex) {
|
|
170
153
|
// Select only one item
|
|
171
|
-
const selectedItems =
|
|
154
|
+
const selectedItems = stateRefs.current.selectedItems;
|
|
172
155
|
selectedItems[0] = rows[rowIndex];
|
|
173
156
|
if (onSelectChange)
|
|
174
157
|
onSelectChange(selectedItems);
|
|
175
158
|
},
|
|
176
159
|
selectAll(checked) {
|
|
177
|
-
const selectedItems =
|
|
160
|
+
const selectedItems = stateRefs.current.selectedItems;
|
|
178
161
|
rows.forEach((row) => {
|
|
179
162
|
const index = selectedItems.findIndex((selectedItem) => selectedItem[idField] === row[idField]);
|
|
180
163
|
if (checked) {
|
|
@@ -189,7 +172,7 @@ export const ScrollerGrid = (props) => {
|
|
|
189
172
|
onSelectChange(selectedItems);
|
|
190
173
|
},
|
|
191
174
|
selectItem(item, checked) {
|
|
192
|
-
const selectedItems =
|
|
175
|
+
const selectedItems = stateRefs.current.selectedItems;
|
|
193
176
|
const index = selectedItems.findIndex((selectedItem) => selectedItem[idField] === item[idField]);
|
|
194
177
|
if (checked) {
|
|
195
178
|
if (index === -1)
|
|
@@ -201,35 +184,15 @@ export const ScrollerGrid = (props) => {
|
|
|
201
184
|
}
|
|
202
185
|
if (onSelectChange)
|
|
203
186
|
onSelectChange(selectedItems);
|
|
204
|
-
},
|
|
205
|
-
reset,
|
|
206
|
-
resetAfterColumnIndex(index, shouldForceUpdate) {
|
|
207
|
-
ref.current?.resetAfterColumnIndex(index, shouldForceUpdate);
|
|
208
|
-
},
|
|
209
|
-
resetAfterIndices(params) {
|
|
210
|
-
ref.current?.resetAfterIndices(params);
|
|
211
|
-
},
|
|
212
|
-
resetAfterRowIndex(index, shouldForceUpdate) {
|
|
213
|
-
ref.current?.resetAfterRowIndex(index, shouldForceUpdate);
|
|
214
187
|
}
|
|
215
|
-
};
|
|
216
|
-
React.useImperativeHandle(mRef, () => instance, [rows]);
|
|
217
|
-
// Force update to work with the new width and rowHeight
|
|
218
|
-
React.useEffect(() => {
|
|
219
|
-
ref.current?.resetAfterIndices({
|
|
220
|
-
columnIndex: 0,
|
|
221
|
-
rowIndex: 0,
|
|
222
|
-
shouldForceUpdate: true
|
|
223
|
-
});
|
|
224
|
-
}, [width, rowHeight]);
|
|
188
|
+
}), [rows]);
|
|
225
189
|
// Rows
|
|
226
|
-
const
|
|
227
|
-
// Row count
|
|
228
|
-
const rowCount = refs.current.hasNextPage ? rowLength + 1 : rowLength;
|
|
190
|
+
const rowCount = rows.length;
|
|
229
191
|
React.useEffect(() => {
|
|
230
192
|
// Auto load data when current page is 0
|
|
231
|
-
if (
|
|
232
|
-
|
|
193
|
+
if (stateRefs.current.queryPaging.currentPage === 0 &&
|
|
194
|
+
stateRefs.current.autoLoad) {
|
|
195
|
+
const initItems = onInitLoad == null ? undefined : onInitLoad(stateRefs.current);
|
|
233
196
|
if (initItems)
|
|
234
197
|
reset(initItems[1], initItems[0]);
|
|
235
198
|
else
|
|
@@ -238,14 +201,17 @@ export const ScrollerGrid = (props) => {
|
|
|
238
201
|
}, [onInitLoad, loadDataLocal]);
|
|
239
202
|
React.useEffect(() => {
|
|
240
203
|
return () => {
|
|
241
|
-
|
|
204
|
+
stateRefs.current.isMounted = false;
|
|
242
205
|
};
|
|
243
206
|
}, []);
|
|
244
207
|
// Layout
|
|
245
|
-
return (_jsxs(React.Fragment, { children: [headerRenderer && headerRenderer(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
208
|
+
return (_jsxs(React.Fragment, { children: [headerRenderer && headerRenderer(stateRefs.current), _jsx(Grid, { cellProps: { rows, states: stateRefs.current }, gridRef: refs, onCellsRendered: (visibleCells, allCells) => {
|
|
209
|
+
// No items, means no necessary to load more data during reset
|
|
210
|
+
if (rowCount > 0 &&
|
|
211
|
+
visibleCells.rowStopIndex + threshold > rowCount) {
|
|
212
|
+
// Auto load next page
|
|
213
|
+
loadDataLocal();
|
|
214
|
+
}
|
|
215
|
+
onCellsRendered?.(visibleCells, allCells);
|
|
216
|
+
}, overscanCount: threshold, rowHeight: rowHeight, rowCount: rowCount, style: style, ...rest }), footerRenderer && footerRenderer(rows, stateRefs.current)] }));
|
|
251
217
|
};
|
|
@@ -1,66 +1,41 @@
|
|
|
1
1
|
import { DataTypes } from "@etsoo/shared";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { ListProps } from "react-window";
|
|
4
4
|
import { GridLoader } from "./GridLoader";
|
|
5
5
|
import { GridMethodRef } from "./GridMethodRef";
|
|
6
|
+
type ScrollerListRowProps<T extends object> = {
|
|
7
|
+
items: T[];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Scroller list forward ref
|
|
11
|
+
*/
|
|
12
|
+
export interface ScrollerListForwardRef<T> extends GridMethodRef<T> {
|
|
13
|
+
}
|
|
6
14
|
/**
|
|
7
15
|
* Scroller vertical list props
|
|
8
16
|
*/
|
|
9
|
-
export type ScrollerListProps<T extends object> = GridLoader<T> & Omit<ListProps<T
|
|
10
|
-
/**
|
|
11
|
-
* Methods ref
|
|
12
|
-
*/
|
|
13
|
-
mRef?: React.Ref<ScrollerListForwardRef<T>>;
|
|
14
|
-
/**
|
|
15
|
-
* Outer div ref
|
|
16
|
-
*/
|
|
17
|
-
oRef?: React.Ref<HTMLDivElement>;
|
|
17
|
+
export type ScrollerListProps<T extends object> = GridLoader<T> & Omit<ListProps<ScrollerListRowProps<T>>, "rowCount" | "rowProps" | "overscanCount"> & {
|
|
18
18
|
/**
|
|
19
19
|
* Height of the list
|
|
20
20
|
*/
|
|
21
|
-
height?: number;
|
|
22
|
-
/**
|
|
23
|
-
* Width of the list
|
|
24
|
-
*/
|
|
25
|
-
width?: number | string;
|
|
21
|
+
height?: number | string;
|
|
26
22
|
/**
|
|
27
23
|
* Id field
|
|
28
24
|
*/
|
|
29
25
|
idField?: DataTypes.Keys<T>;
|
|
30
26
|
/**
|
|
31
|
-
*
|
|
27
|
+
* Methods ref
|
|
32
28
|
*/
|
|
33
|
-
|
|
29
|
+
mRef?: React.Ref<ScrollerListForwardRef<T>>;
|
|
34
30
|
/**
|
|
35
|
-
*
|
|
31
|
+
* Width of the list
|
|
36
32
|
*/
|
|
37
|
-
|
|
33
|
+
width?: number | string;
|
|
38
34
|
};
|
|
39
|
-
/**
|
|
40
|
-
* Scroller list ref
|
|
41
|
-
*/
|
|
42
|
-
export interface ScrollerListRef {
|
|
43
|
-
/**
|
|
44
|
-
* Scroll to the specified offset (scrollTop or scrollLeft, depending on the direction prop).
|
|
45
|
-
*/
|
|
46
|
-
scrollTo(scrollOffset: number): void;
|
|
47
|
-
/**
|
|
48
|
-
* Scroll to the specified item.
|
|
49
|
-
*/
|
|
50
|
-
scrollToItem(index: number, align?: Align): void;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Scroller list forward ref
|
|
54
|
-
*/
|
|
55
|
-
export interface ScrollerListForwardRef<T> extends GridMethodRef<T> {
|
|
56
|
-
/**
|
|
57
|
-
* Refresh latest page data
|
|
58
|
-
*/
|
|
59
|
-
refresh(): void;
|
|
60
|
-
}
|
|
61
35
|
/**
|
|
62
36
|
* Scroller vertical list
|
|
63
37
|
* @param props Props
|
|
64
38
|
* @returns Component
|
|
65
39
|
*/
|
|
66
40
|
export declare const ScrollerList: <T extends object>(props: ScrollerListProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
41
|
+
export {};
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Utils } from "@etsoo/shared";
|
|
3
3
|
import React from "react";
|
|
4
|
-
import {
|
|
4
|
+
import { List, useListRef } from "react-window";
|
|
5
5
|
import { useCombinedRefs } from "../uses/useCombinedRefs";
|
|
6
|
-
import { GridSizeGet } from "./GridLoader";
|
|
7
6
|
// Calculate loadBatchSize
|
|
8
|
-
const calculateBatchSize = (height,
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const calculateBatchSize = (height, rowHeight) => {
|
|
8
|
+
if (typeof height === "number" &&
|
|
9
|
+
typeof rowHeight === "number" &&
|
|
10
|
+
rowHeight > 0) {
|
|
11
|
+
return 1 + Math.ceil(height / rowHeight);
|
|
12
|
+
}
|
|
13
|
+
return 10;
|
|
11
14
|
};
|
|
12
15
|
/**
|
|
13
16
|
* Scroller vertical list
|
|
@@ -16,18 +19,14 @@ const calculateBatchSize = (height, itemSize) => {
|
|
|
16
19
|
*/
|
|
17
20
|
export const ScrollerList = (props) => {
|
|
18
21
|
// Destruct
|
|
19
|
-
const { autoLoad = true, defaultOrderBy, height =
|
|
22
|
+
const { autoLoad = true, defaultOrderBy, height = "100%", width = "100%", mRef, style = {}, idField = "id", rowHeight, listRef, loadBatchSize = calculateBatchSize(height, rowHeight), loadData, threshold = 3, onRowsRendered, onInitLoad, onUpdateRows, ...rest } = props;
|
|
20
23
|
// Style
|
|
21
24
|
Object.assign(style, {
|
|
22
|
-
width
|
|
23
|
-
height
|
|
24
|
-
display: "inline-block"
|
|
25
|
+
width,
|
|
26
|
+
height
|
|
25
27
|
});
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
const outerRef = React.useRef(null);
|
|
29
|
-
const refs = useCombinedRefs(oRef, outerRef);
|
|
30
|
-
// Rows
|
|
28
|
+
const localRef = useListRef(null);
|
|
29
|
+
const refs = useCombinedRefs(listRef, localRef);
|
|
31
30
|
const [rows, updateRows] = React.useState([]);
|
|
32
31
|
const setRows = (rows, reset = false) => {
|
|
33
32
|
stateRefs.current.loadedItems = rows.length;
|
|
@@ -35,8 +34,7 @@ export const ScrollerList = (props) => {
|
|
|
35
34
|
if (!reset && onUpdateRows)
|
|
36
35
|
onUpdateRows(rows, stateRefs.current);
|
|
37
36
|
};
|
|
38
|
-
|
|
39
|
-
const batchSize = GridSizeGet(loadBatchSize, height);
|
|
37
|
+
const batchSize = Utils.getResult(loadBatchSize, height);
|
|
40
38
|
const stateRefs = React.useRef({
|
|
41
39
|
queryPaging: {
|
|
42
40
|
currentPage: 0,
|
|
@@ -107,13 +105,6 @@ export const ScrollerList = (props) => {
|
|
|
107
105
|
}
|
|
108
106
|
});
|
|
109
107
|
};
|
|
110
|
-
const itemRendererLocal = (itemProps) => {
|
|
111
|
-
// Custom render
|
|
112
|
-
return itemRenderer({
|
|
113
|
-
...itemProps,
|
|
114
|
-
data: rows[itemProps.index]
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
108
|
// Reset the state and load again
|
|
118
109
|
const reset = (add, items = []) => {
|
|
119
110
|
const { queryPaging, ...rest } = add ?? {};
|
|
@@ -136,7 +127,6 @@ export const ScrollerList = (props) => {
|
|
|
136
127
|
setRows(items, true);
|
|
137
128
|
};
|
|
138
129
|
React.useImperativeHandle(mRef, () => {
|
|
139
|
-
const refMethods = listRef.current;
|
|
140
130
|
return {
|
|
141
131
|
delete(index) {
|
|
142
132
|
const item = rows.at(index);
|
|
@@ -156,47 +146,38 @@ export const ScrollerList = (props) => {
|
|
|
156
146
|
loadDataLocal(0);
|
|
157
147
|
},
|
|
158
148
|
reset,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
},
|
|
162
|
-
scrollToItemRef(index, align) {
|
|
163
|
-
refMethods.scrollToItem(index, align);
|
|
149
|
+
scrollToRow(param) {
|
|
150
|
+
localRef.current?.scrollToRow(param);
|
|
164
151
|
}
|
|
165
152
|
};
|
|
166
153
|
}, []);
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
loadDataLocal();
|
|
175
|
-
}
|
|
176
|
-
// Custom
|
|
177
|
-
if (onItemsRendered)
|
|
178
|
-
onItemsRendered(props);
|
|
179
|
-
};
|
|
180
|
-
// Item count
|
|
181
|
-
const itemCount = stateRefs.current.hasNextPage ? rowCount + 1 : rowCount;
|
|
154
|
+
// When layout ready
|
|
155
|
+
React.useEffect(() => {
|
|
156
|
+
// Return clear function
|
|
157
|
+
return () => {
|
|
158
|
+
stateRefs.current.isMounted = false;
|
|
159
|
+
};
|
|
160
|
+
}, []);
|
|
182
161
|
React.useEffect(() => {
|
|
183
162
|
// Auto load data when current page is 0
|
|
184
163
|
if (stateRefs.current.queryPaging?.currentPage === 0 &&
|
|
185
164
|
stateRefs.current.autoLoad) {
|
|
186
|
-
const initItems = onInitLoad == null ? undefined : onInitLoad(
|
|
165
|
+
const initItems = onInitLoad == null ? undefined : onInitLoad(localRef.current);
|
|
187
166
|
if (initItems)
|
|
188
167
|
reset(initItems[1], initItems[0]);
|
|
189
168
|
else
|
|
190
169
|
loadDataLocal();
|
|
191
170
|
}
|
|
192
171
|
}, [onInitLoad, loadDataLocal]);
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
// Return clear function
|
|
196
|
-
return () => {
|
|
197
|
-
stateRefs.current.isMounted = false;
|
|
198
|
-
};
|
|
199
|
-
}, []);
|
|
172
|
+
// Row count
|
|
173
|
+
const rowCount = rows.length;
|
|
200
174
|
// Layout
|
|
201
|
-
return
|
|
175
|
+
return (_jsx(List, { listRef: refs, onRowsRendered: (visibleCells, allCells) => {
|
|
176
|
+
// No items, means no necessary to load more data during reset
|
|
177
|
+
if (rowCount > 0 && visibleCells.stopIndex + threshold > rowCount) {
|
|
178
|
+
// Auto load next page
|
|
179
|
+
loadDataLocal();
|
|
180
|
+
}
|
|
181
|
+
onRowsRendered?.(visibleCells, allCells);
|
|
182
|
+
}, overscanCount: threshold, rowHeight: rowHeight, rowCount: rowCount, rowProps: { items: rows }, style: style, ...rest }));
|
|
202
183
|
};
|
package/lib/mjs/index.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ export * from "./components/ListItemReact";
|
|
|
10
10
|
export * from "./components/ScrollerGrid";
|
|
11
11
|
export * from "./components/ScrollerList";
|
|
12
12
|
export * from "./components/ScrollRestoration";
|
|
13
|
-
export type { ListOnScrollProps, GridOnScrollProps, VariableSizeGrid } from "react-window";
|
|
14
13
|
export * from "./custom/CustomFieldReact";
|
|
15
14
|
export * from "./notifier/Notifier";
|
|
16
15
|
export * from "@etsoo/notificationbase";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/react",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.53",
|
|
4
4
|
"description": "TypeScript ReactJs UI Independent Framework",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"module": "lib/mjs/index.js",
|
|
@@ -38,27 +38,27 @@
|
|
|
38
38
|
"@emotion/css": "^11.13.5",
|
|
39
39
|
"@emotion/react": "^11.14.0",
|
|
40
40
|
"@emotion/styled": "^11.14.1",
|
|
41
|
-
"@etsoo/appscript": "^1.6.
|
|
42
|
-
"@etsoo/notificationbase": "^1.1.
|
|
43
|
-
"@etsoo/shared": "^1.2.
|
|
41
|
+
"@etsoo/appscript": "^1.6.45",
|
|
42
|
+
"@etsoo/notificationbase": "^1.1.64",
|
|
43
|
+
"@etsoo/shared": "^1.2.76",
|
|
44
44
|
"react": "^19.1.1",
|
|
45
45
|
"react-dom": "^19.1.1",
|
|
46
|
-
"react-router-dom": "^7.
|
|
47
|
-
"react-window": "^1.
|
|
46
|
+
"react-router-dom": "^7.9.1",
|
|
47
|
+
"react-window": "^2.1.1"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@babel/cli": "^7.28.3",
|
|
51
|
-
"@babel/core": "^7.28.
|
|
51
|
+
"@babel/core": "^7.28.4",
|
|
52
52
|
"@babel/plugin-transform-runtime": "^7.28.3",
|
|
53
53
|
"@babel/preset-env": "^7.28.3",
|
|
54
|
-
"@babel/runtime-corejs3": "^7.28.
|
|
55
|
-
"@testing-library/jest-dom": "^6.
|
|
54
|
+
"@babel/runtime-corejs3": "^7.28.4",
|
|
55
|
+
"@testing-library/jest-dom": "^6.8.0",
|
|
56
56
|
"@testing-library/react": "^16.3.0",
|
|
57
|
-
"@types/react": "^19.1.
|
|
58
|
-
"@types/react-dom": "^19.1.
|
|
59
|
-
"@types/react-window": "^
|
|
60
|
-
"@vitejs/plugin-react": "^5.0.
|
|
61
|
-
"jsdom": "^
|
|
57
|
+
"@types/react": "^19.1.13",
|
|
58
|
+
"@types/react-dom": "^19.1.9",
|
|
59
|
+
"@types/react-window": "^2.0.0",
|
|
60
|
+
"@vitejs/plugin-react": "^5.0.3",
|
|
61
|
+
"jsdom": "^27.0.0",
|
|
62
62
|
"typescript": "^5.9.2",
|
|
63
63
|
"vitest": "^3.2.4"
|
|
64
64
|
}
|
package/setupTests.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
|
4
|
+
|
|
5
|
+
// Mock the ResizeObserver
|
|
6
|
+
const ResizeObserverMock = vi.fn(() => ({
|
|
7
|
+
observe: vi.fn(),
|
|
8
|
+
unobserve: vi.fn(),
|
|
9
|
+
disconnect: vi.fn()
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
// Stub the global ResizeObserver
|
|
13
|
+
vi.stubGlobal("ResizeObserver", ResizeObserverMock);
|
|
@@ -4,7 +4,7 @@ import { DataTypes, DomUtils } from "@etsoo/shared";
|
|
|
4
4
|
/**
|
|
5
5
|
* Grid size
|
|
6
6
|
*/
|
|
7
|
-
export type GridSize = number | ((input: number) => number);
|
|
7
|
+
export type GridSize = number | ((input: number | string) => number);
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Grid size calculation
|
|
@@ -132,7 +132,7 @@ export type GridLoader<
|
|
|
132
132
|
defaultOrderBy?: QueryPagingOrder[];
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
|
-
* Batch size when load data, default will be calcuated with height and
|
|
135
|
+
* Batch size when load data, default will be calcuated with height and rowHeight
|
|
136
136
|
*/
|
|
137
137
|
loadBatchSize?: GridSize;
|
|
138
138
|
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ListImperativeAPI } from "react-window";
|
|
2
2
|
import { GridLoaderPartialStates } from "./GridLoader";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Scroll to row parameter type
|
|
6
|
+
*/
|
|
7
|
+
export type ScrollToRowParam = Parameters<ListImperativeAPI["scrollToRow"]>[0];
|
|
8
|
+
|
|
4
9
|
/**
|
|
5
10
|
* Grid method ref
|
|
6
11
|
*/
|
|
@@ -19,18 +24,19 @@ export interface GridMethodRef<T> {
|
|
|
19
24
|
insert(item: T, start: number): void;
|
|
20
25
|
|
|
21
26
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @param add Additional data
|
|
27
|
+
* Refresh latest page data
|
|
24
28
|
*/
|
|
25
|
-
|
|
29
|
+
refresh(): void;
|
|
26
30
|
|
|
27
31
|
/**
|
|
28
|
-
*
|
|
32
|
+
* Reset
|
|
33
|
+
* @param add Additional data
|
|
29
34
|
*/
|
|
30
|
-
|
|
35
|
+
reset(add?: GridLoaderPartialStates<T>): void;
|
|
31
36
|
|
|
32
37
|
/**
|
|
33
|
-
* Scroll to the
|
|
38
|
+
* Scroll to the row
|
|
39
|
+
* @param param Parameters to control
|
|
34
40
|
*/
|
|
35
|
-
|
|
41
|
+
scrollToRow(param: ScrollToRowParam): void;
|
|
36
42
|
}
|