@etsoo/react 1.8.50 → 1.8.52
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/custom/CustomFieldReact.d.ts +1 -1
- 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/custom/CustomFieldReact.d.ts +1 -1
- package/lib/mjs/index.d.ts +0 -1
- package/package.json +13 -13
- 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/custom/CustomFieldReact.ts +1 -1
- package/src/index.ts +0 -5
- package/vite.config.mts +1 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React, { act } from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
import { screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import { Grid } from "react-window";
|
|
5
|
+
import { GridColumn, ScrollerGrid } from "../src";
|
|
6
|
+
|
|
7
|
+
// List item
|
|
8
|
+
type ListItem = {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const rows: ListItem[] = [
|
|
14
|
+
{ id: "1", name: "Item 1" },
|
|
15
|
+
{ id: "2", name: "Item 2" },
|
|
16
|
+
{ id: "3", name: "Item 3" },
|
|
17
|
+
{ id: "4", name: "Item 4" },
|
|
18
|
+
{ id: "5", name: "Item 5" }
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const columns: GridColumn<ListItem>[] = [
|
|
22
|
+
{ field: "id", header: "ID", width: 100 },
|
|
23
|
+
{ field: "name", header: "Name", width: 200 }
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const columnCount = columns.length;
|
|
27
|
+
const columnWidth = (index: number, cellProps: { rows: ListItem[] }) => {
|
|
28
|
+
return columns[index].width ?? 0;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Root
|
|
32
|
+
const root = document.body;
|
|
33
|
+
const container: HTMLElement = document.createElement("div");
|
|
34
|
+
root.append(container);
|
|
35
|
+
|
|
36
|
+
const reactRoot = createRoot(container);
|
|
37
|
+
|
|
38
|
+
test("Tests for original Grid", async () => {
|
|
39
|
+
act(() => {
|
|
40
|
+
reactRoot.render(
|
|
41
|
+
<Grid<{ rows: ListItem[] }>
|
|
42
|
+
className="test"
|
|
43
|
+
defaultHeight={200}
|
|
44
|
+
onCellsRendered={(visibleCells, allCells) => {
|
|
45
|
+
expect(visibleCells.rowStartIndex).toBe(0);
|
|
46
|
+
expect(visibleCells.rowStopIndex).toBe(3);
|
|
47
|
+
}}
|
|
48
|
+
cellComponent={({ columnIndex, rowIndex, style, rows }) => (
|
|
49
|
+
<h1 id={`id${rowIndex}`} style={style}>
|
|
50
|
+
{rows[rowIndex][columns[columnIndex].field ?? "id"]}
|
|
51
|
+
</h1>
|
|
52
|
+
)}
|
|
53
|
+
rowCount={rows.length}
|
|
54
|
+
columnCount={columnCount}
|
|
55
|
+
columnWidth={columnWidth}
|
|
56
|
+
rowHeight={50}
|
|
57
|
+
cellProps={{ rows }}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const element = await screen.findByText("Item 4");
|
|
63
|
+
expect(element.id).toBe("id3");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("Tests for custom Grid", async () => {
|
|
67
|
+
act(() => {
|
|
68
|
+
reactRoot.render(
|
|
69
|
+
<ScrollerGrid<ListItem>
|
|
70
|
+
loadData={(props, lastItem) => {
|
|
71
|
+
expect(props.queryPaging.currentPage).toBe(0);
|
|
72
|
+
expect(props.queryPaging.batchSize).toBe(10);
|
|
73
|
+
|
|
74
|
+
return new Promise((resolve) => resolve(rows));
|
|
75
|
+
}}
|
|
76
|
+
columnCount={columnCount}
|
|
77
|
+
columnWidth={columnWidth}
|
|
78
|
+
defaultHeight={200}
|
|
79
|
+
rowHeight={50}
|
|
80
|
+
cellComponent={({ columnIndex, rowIndex, style, rows }) => {
|
|
81
|
+
return (
|
|
82
|
+
<h1 id={`id${rowIndex}`} style={style}>
|
|
83
|
+
{rows[rowIndex][columns[columnIndex].field ?? "id"]}
|
|
84
|
+
</h1>
|
|
85
|
+
);
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
await waitFor(() => {
|
|
92
|
+
const item = screen.getByText("Item 3");
|
|
93
|
+
expect(item.id).toBe("id2");
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React, { act } from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
import { screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import { List } from "react-window";
|
|
5
|
+
import { ScrollerList } from "../src";
|
|
6
|
+
|
|
7
|
+
// List item
|
|
8
|
+
type ListItem = {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const items: ListItem[] = [
|
|
14
|
+
{ id: "1", name: "Item 1" },
|
|
15
|
+
{ id: "2", name: "Item 2" },
|
|
16
|
+
{ id: "3", name: "Item 3" },
|
|
17
|
+
{ id: "4", name: "Item 4" },
|
|
18
|
+
{ id: "5", name: "Item 5" }
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
// Root
|
|
22
|
+
const root = document.body;
|
|
23
|
+
const container: HTMLElement = document.createElement("div");
|
|
24
|
+
root.append(container);
|
|
25
|
+
|
|
26
|
+
const reactRoot = createRoot(container);
|
|
27
|
+
|
|
28
|
+
test("Tests for original List", async () => {
|
|
29
|
+
act(() => {
|
|
30
|
+
reactRoot.render(
|
|
31
|
+
<List<{ items: ListItem[] }>
|
|
32
|
+
className="test"
|
|
33
|
+
defaultHeight={200}
|
|
34
|
+
onRowsRendered={({ startIndex, stopIndex }) => {
|
|
35
|
+
expect(startIndex).toBe(0);
|
|
36
|
+
expect(stopIndex).toBe(3);
|
|
37
|
+
}}
|
|
38
|
+
rowComponent={({ index, style, items }) => (
|
|
39
|
+
<h1 id={`id${index}`} style={style}>
|
|
40
|
+
{items[index].id} / {items[index].name}
|
|
41
|
+
</h1>
|
|
42
|
+
)}
|
|
43
|
+
rowCount={items.length}
|
|
44
|
+
rowHeight={50}
|
|
45
|
+
rowProps={{ items }}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const element = await screen.findByText("4 / Item 4");
|
|
51
|
+
expect(element.id).toBe("id3");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("Tests for custom List", async () => {
|
|
55
|
+
act(() => {
|
|
56
|
+
reactRoot.render(
|
|
57
|
+
<ScrollerList<ListItem>
|
|
58
|
+
defaultHeight={200}
|
|
59
|
+
loadData={(props, lastItem) => {
|
|
60
|
+
expect(props.queryPaging.currentPage).toBe(0);
|
|
61
|
+
expect(props.queryPaging.batchSize).toBe(10);
|
|
62
|
+
|
|
63
|
+
return new Promise((resolve) => resolve(items));
|
|
64
|
+
}}
|
|
65
|
+
onRowsRendered={({ startIndex, stopIndex }) => {
|
|
66
|
+
expect(startIndex).toBe(0);
|
|
67
|
+
expect(stopIndex).toBe(3);
|
|
68
|
+
}}
|
|
69
|
+
rowHeight={50}
|
|
70
|
+
rowComponent={({ index, style, items }) => {
|
|
71
|
+
return (
|
|
72
|
+
<h1 id={`id${index}`} style={style}>
|
|
73
|
+
{items[index].id} / {items[index].name}
|
|
74
|
+
</h1>
|
|
75
|
+
);
|
|
76
|
+
}}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await waitFor(() => {
|
|
82
|
+
const item = screen.getByText("3 / Item 3");
|
|
83
|
+
expect(item.id).toBe("id2");
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -3,7 +3,7 @@ import { DataTypes } from "@etsoo/shared";
|
|
|
3
3
|
/**
|
|
4
4
|
* Grid size
|
|
5
5
|
*/
|
|
6
|
-
export type GridSize = number | ((input: number) => number);
|
|
6
|
+
export type GridSize = number | ((input: number | string) => number);
|
|
7
7
|
/**
|
|
8
8
|
* Grid size calculation
|
|
9
9
|
* @param size Size
|
|
@@ -81,7 +81,7 @@ export type GridLoader<T extends object, P extends GridJsonData = GridLoadDataPr
|
|
|
81
81
|
*/
|
|
82
82
|
defaultOrderBy?: QueryPagingOrder[];
|
|
83
83
|
/**
|
|
84
|
-
* Batch size when load data, default will be calcuated with height and
|
|
84
|
+
* Batch size when load data, default will be calcuated with height and rowHeight
|
|
85
85
|
*/
|
|
86
86
|
loadBatchSize?: GridSize;
|
|
87
87
|
/**
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ListImperativeAPI } from "react-window";
|
|
2
2
|
import { GridLoaderPartialStates } from "./GridLoader";
|
|
3
|
+
/**
|
|
4
|
+
* Scroll to row parameter type
|
|
5
|
+
*/
|
|
6
|
+
export type ScrollToRowParam = Parameters<ListImperativeAPI["scrollToRow"]>[0];
|
|
3
7
|
/**
|
|
4
8
|
* Grid method ref
|
|
5
9
|
*/
|
|
@@ -15,17 +19,18 @@ export interface GridMethodRef<T> {
|
|
|
15
19
|
* @param start Start position
|
|
16
20
|
*/
|
|
17
21
|
insert(item: T, start: number): void;
|
|
22
|
+
/**
|
|
23
|
+
* Refresh latest page data
|
|
24
|
+
*/
|
|
25
|
+
refresh(): void;
|
|
18
26
|
/**
|
|
19
27
|
* Reset
|
|
20
28
|
* @param add Additional data
|
|
21
29
|
*/
|
|
22
30
|
reset(add?: GridLoaderPartialStates<T>): void;
|
|
23
31
|
/**
|
|
24
|
-
* Scroll to the
|
|
25
|
-
|
|
26
|
-
scrollToRef(scrollOffset: number): void;
|
|
27
|
-
/**
|
|
28
|
-
* Scroll to the specified item.
|
|
32
|
+
* Scroll to the row
|
|
33
|
+
* @param param Parameters to control
|
|
29
34
|
*/
|
|
30
|
-
|
|
35
|
+
scrollToRow(param: ScrollToRowParam): void;
|
|
31
36
|
}
|
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
import { DataTypes } from "@etsoo/shared";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { GridProps } from "react-window";
|
|
4
4
|
import { GridJsonData, GridLoadDataProps, GridLoader, GridLoaderStates } from "./GridLoader";
|
|
5
|
-
import { GridMethodRef } from "./GridMethodRef";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Data
|
|
18
|
-
*/
|
|
19
|
-
data?: T;
|
|
5
|
+
import { GridMethodRef, ScrollToRowParam } from "./GridMethodRef";
|
|
6
|
+
type ScrollerGridCellrops<T extends object> = {
|
|
7
|
+
rows: T[];
|
|
8
|
+
states: GridLoaderStates<T>;
|
|
9
|
+
};
|
|
10
|
+
export type ScrollToCellParam = {
|
|
11
|
+
behavior?: ScrollToRowParam["behavior"];
|
|
12
|
+
columnAlign?: ScrollToRowParam["align"];
|
|
13
|
+
columnIndex: number;
|
|
14
|
+
rowAlign?: ScrollToRowParam["align"];
|
|
15
|
+
rowIndex: number;
|
|
20
16
|
};
|
|
21
17
|
/**
|
|
22
18
|
* Scroller vertical grid props
|
|
23
19
|
*/
|
|
24
|
-
export type ScrollerGridProps<T extends object, P extends GridJsonData = GridLoadDataProps> = GridLoader<T, P> & Omit<
|
|
20
|
+
export type ScrollerGridProps<T extends object, P extends GridJsonData = GridLoadDataProps> = GridLoader<T, P> & Omit<GridProps<ScrollerGridCellrops<T>>, "cellProps" | "overscanCount" | "rowCount"> & {
|
|
25
21
|
/**
|
|
26
22
|
* Footer renderer
|
|
27
23
|
*/
|
|
@@ -31,13 +27,13 @@ export type ScrollerGridProps<T extends object, P extends GridJsonData = GridLoa
|
|
|
31
27
|
*/
|
|
32
28
|
headerRenderer?: (states: GridLoaderStates<T>) => React.ReactNode;
|
|
33
29
|
/**
|
|
34
|
-
*
|
|
30
|
+
* Height of the grid
|
|
35
31
|
*/
|
|
36
|
-
|
|
32
|
+
height?: number | string;
|
|
37
33
|
/**
|
|
38
|
-
*
|
|
34
|
+
* Id field
|
|
39
35
|
*/
|
|
40
|
-
|
|
36
|
+
idField?: DataTypes.Keys<T>;
|
|
41
37
|
/**
|
|
42
38
|
* Methods
|
|
43
39
|
*/
|
|
@@ -47,34 +43,24 @@ export type ScrollerGridProps<T extends object, P extends GridJsonData = GridLoa
|
|
|
47
43
|
*/
|
|
48
44
|
onSelectChange?: (selectedItems: T[]) => void;
|
|
49
45
|
/**
|
|
50
|
-
*
|
|
46
|
+
* Width of the grid
|
|
51
47
|
*/
|
|
52
|
-
|
|
48
|
+
width?: number | string;
|
|
53
49
|
};
|
|
54
50
|
/**
|
|
55
51
|
* Scroller grid forward ref
|
|
56
52
|
*/
|
|
57
53
|
export interface ScrollerGridForwardRef<T> extends GridMethodRef<T> {
|
|
58
54
|
/**
|
|
59
|
-
* Scroll to the
|
|
55
|
+
* Scroll to the cell
|
|
56
|
+
* @param param Parameters to control
|
|
60
57
|
*/
|
|
61
|
-
|
|
62
|
-
scrollLeft: number;
|
|
63
|
-
scrollTop: number;
|
|
64
|
-
}): void;
|
|
65
|
-
scrollToItem(params: {
|
|
66
|
-
align?: Align | undefined;
|
|
67
|
-
columnIndex?: number | undefined;
|
|
68
|
-
rowIndex?: number | undefined;
|
|
69
|
-
}): void;
|
|
58
|
+
scrollToCell(param: ScrollToCellParam): void;
|
|
70
59
|
/**
|
|
71
|
-
* Scroll to the
|
|
60
|
+
* Scroll to the cell
|
|
61
|
+
* @param param Parameters to control
|
|
72
62
|
*/
|
|
73
|
-
|
|
74
|
-
align?: Align | undefined;
|
|
75
|
-
columnIndex?: number | undefined;
|
|
76
|
-
rowIndex?: number | undefined;
|
|
77
|
-
}): void;
|
|
63
|
+
scrollToColumn(param: ScrollToRowParam): void;
|
|
78
64
|
/**
|
|
79
65
|
* Select the item
|
|
80
66
|
* @param rowIndex Row index
|
|
@@ -91,18 +77,6 @@ export interface ScrollerGridForwardRef<T> extends GridMethodRef<T> {
|
|
|
91
77
|
* @param checked Checked
|
|
92
78
|
*/
|
|
93
79
|
selectItem(item: any, checked: boolean): void;
|
|
94
|
-
/**
|
|
95
|
-
*
|
|
96
|
-
* @param index
|
|
97
|
-
* @param shouldForceUpdate
|
|
98
|
-
*/
|
|
99
|
-
resetAfterColumnIndex(index: number, shouldForceUpdate?: boolean): void;
|
|
100
|
-
resetAfterIndices(params: {
|
|
101
|
-
columnIndex: number;
|
|
102
|
-
rowIndex: number;
|
|
103
|
-
shouldForceUpdate?: boolean | undefined;
|
|
104
|
-
}): void;
|
|
105
|
-
resetAfterRowIndex(index: number, shouldForceUpdate?: boolean): void;
|
|
106
80
|
}
|
|
107
81
|
/**
|
|
108
82
|
* Scroller vertical grid
|
|
@@ -110,3 +84,4 @@ export interface ScrollerGridForwardRef<T> extends GridMethodRef<T> {
|
|
|
110
84
|
* @returns Component
|
|
111
85
|
*/
|
|
112
86
|
export declare const ScrollerGrid: <T extends object>(props: ScrollerGridProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
87
|
+
export {};
|
|
@@ -7,6 +7,7 @@ exports.ScrollerGrid = void 0;
|
|
|
7
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
8
|
const react_1 = __importDefault(require("react"));
|
|
9
9
|
const react_window_1 = require("react-window");
|
|
10
|
+
const useCombinedRefs_1 = require("../uses/useCombinedRefs");
|
|
10
11
|
/**
|
|
11
12
|
* Scroller vertical grid
|
|
12
13
|
* @param props Props
|
|
@@ -14,17 +15,26 @@ const react_window_1 = require("react-window");
|
|
|
14
15
|
*/
|
|
15
16
|
const ScrollerGrid = (props) => {
|
|
16
17
|
// Destruct
|
|
17
|
-
const { autoLoad = true, defaultOrderBy, footerRenderer, headerRenderer,
|
|
18
|
+
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;
|
|
19
|
+
// Style
|
|
20
|
+
Object.assign(style, {
|
|
21
|
+
width,
|
|
22
|
+
height,
|
|
23
|
+
overflowX: "hidden"
|
|
24
|
+
});
|
|
25
|
+
// Refs
|
|
26
|
+
const localRef = (0, react_window_1.useGridRef)(null);
|
|
27
|
+
const refs = (0, useCombinedRefs_1.useCombinedRefs)(gridRef, localRef);
|
|
18
28
|
// Rows
|
|
19
29
|
const [rows, updateRows] = react_1.default.useState([]);
|
|
20
30
|
const setRows = (rows, reset = false) => {
|
|
21
|
-
|
|
31
|
+
stateRefs.current.loadedItems = rows.length;
|
|
22
32
|
updateRows(rows);
|
|
23
33
|
if (!reset && onUpdateRows)
|
|
24
|
-
onUpdateRows(rows,
|
|
34
|
+
onUpdateRows(rows, stateRefs.current);
|
|
25
35
|
};
|
|
26
|
-
// Refs
|
|
27
|
-
const
|
|
36
|
+
// State Refs
|
|
37
|
+
const stateRefs = react_1.default.useRef({
|
|
28
38
|
queryPaging: {
|
|
29
39
|
currentPage: 0,
|
|
30
40
|
orderBy: defaultOrderBy,
|
|
@@ -37,58 +47,58 @@ const ScrollerGrid = (props) => {
|
|
|
37
47
|
selectedItems: [],
|
|
38
48
|
idCache: {}
|
|
39
49
|
});
|
|
40
|
-
const ref = react_1.default.useRef(null);
|
|
41
50
|
// Load data
|
|
42
51
|
const loadDataLocal = (pageAdd = 1) => {
|
|
43
52
|
// Prevent multiple loadings
|
|
44
|
-
if (!
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
if (!stateRefs.current.hasNextPage ||
|
|
54
|
+
stateRefs.current.isNextPageLoading ||
|
|
55
|
+
stateRefs.current.isMounted === false)
|
|
47
56
|
return;
|
|
48
57
|
// Update state
|
|
49
|
-
|
|
58
|
+
stateRefs.current.isNextPageLoading = true;
|
|
50
59
|
// Parameters
|
|
51
|
-
const { queryPaging, data } =
|
|
60
|
+
const { queryPaging, data } = stateRefs.current;
|
|
52
61
|
const loadProps = {
|
|
53
62
|
queryPaging,
|
|
54
63
|
data
|
|
55
64
|
};
|
|
56
|
-
loadData(loadProps,
|
|
57
|
-
if (result == null ||
|
|
65
|
+
loadData(loadProps, stateRefs.current.lastItem).then((result) => {
|
|
66
|
+
if (result == null || stateRefs.current.isMounted === false) {
|
|
58
67
|
return;
|
|
59
68
|
}
|
|
60
|
-
|
|
69
|
+
stateRefs.current.isMounted = true;
|
|
61
70
|
const newItems = result.length;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
stateRefs.current.lastLoadedItems = newItems;
|
|
72
|
+
stateRefs.current.lastItem = result.at(-1);
|
|
73
|
+
stateRefs.current.isNextPageLoading = false;
|
|
74
|
+
stateRefs.current.hasNextPage =
|
|
75
|
+
newItems >= stateRefs.current.queryPaging.batchSize;
|
|
66
76
|
if (pageAdd === 0) {
|
|
67
77
|
// New items
|
|
68
|
-
const newRows =
|
|
78
|
+
const newRows = stateRefs.current.lastLoadedItems
|
|
69
79
|
? [...rows]
|
|
70
|
-
.splice(rows.length -
|
|
80
|
+
.splice(rows.length - stateRefs.current.lastLoadedItems, stateRefs.current.lastLoadedItems)
|
|
71
81
|
.concat(result)
|
|
72
82
|
: result;
|
|
73
|
-
|
|
83
|
+
stateRefs.current.idCache = {};
|
|
74
84
|
for (const row of newRows) {
|
|
75
85
|
const id = row[idField];
|
|
76
|
-
|
|
86
|
+
stateRefs.current.idCache[id] = null;
|
|
77
87
|
}
|
|
78
88
|
// Update rows
|
|
79
89
|
setRows(newRows);
|
|
80
90
|
}
|
|
81
91
|
else {
|
|
82
92
|
// Set current page
|
|
83
|
-
if (
|
|
84
|
-
|
|
93
|
+
if (stateRefs.current.queryPaging.currentPage == null)
|
|
94
|
+
stateRefs.current.queryPaging.currentPage = pageAdd;
|
|
85
95
|
else
|
|
86
|
-
|
|
96
|
+
stateRefs.current.queryPaging.currentPage += pageAdd;
|
|
87
97
|
// Update rows, avoid duplicate items
|
|
88
98
|
const newRows = [...rows];
|
|
89
99
|
for (const item of result) {
|
|
90
100
|
const id = item[idField];
|
|
91
|
-
if (
|
|
101
|
+
if (stateRefs.current.idCache[id] === undefined) {
|
|
92
102
|
newRows.push(item);
|
|
93
103
|
}
|
|
94
104
|
}
|
|
@@ -96,34 +106,6 @@ const ScrollerGrid = (props) => {
|
|
|
96
106
|
}
|
|
97
107
|
});
|
|
98
108
|
};
|
|
99
|
-
// Item renderer
|
|
100
|
-
const itemRendererLocal = (itemProps, state) => {
|
|
101
|
-
// Custom render
|
|
102
|
-
const data = itemProps.rowIndex < rows.length ? rows[itemProps.rowIndex] : undefined;
|
|
103
|
-
return itemRenderer({
|
|
104
|
-
...itemProps,
|
|
105
|
-
data,
|
|
106
|
-
selectedItems: state.selectedItems,
|
|
107
|
-
setItems: (callback) => {
|
|
108
|
-
const result = callback(rows, instance);
|
|
109
|
-
if (result == null)
|
|
110
|
-
return;
|
|
111
|
-
setRows(result);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
};
|
|
115
|
-
// Local items renderer callback
|
|
116
|
-
const onItemsRenderedLocal = (props) => {
|
|
117
|
-
// No items, means no necessary to load more data during reset
|
|
118
|
-
const itemCount = rows.length;
|
|
119
|
-
if (itemCount > 0 && props.visibleRowStopIndex + threshold > itemCount) {
|
|
120
|
-
// Auto load next page
|
|
121
|
-
loadDataLocal();
|
|
122
|
-
}
|
|
123
|
-
// Custom
|
|
124
|
-
if (onItemsRendered)
|
|
125
|
-
onItemsRendered(props);
|
|
126
|
-
};
|
|
127
109
|
// Reset the state and load again
|
|
128
110
|
const reset = (add, items = []) => {
|
|
129
111
|
const { queryPaging, ...rest } = add ?? {};
|
|
@@ -136,16 +118,16 @@ const ScrollerGrid = (props) => {
|
|
|
136
118
|
lastItem: undefined,
|
|
137
119
|
...rest
|
|
138
120
|
};
|
|
139
|
-
Object.assign(
|
|
140
|
-
Object.assign(
|
|
121
|
+
Object.assign(stateRefs.current, resetState);
|
|
122
|
+
Object.assign(stateRefs.current.queryPaging, {
|
|
141
123
|
currentPage: 0,
|
|
142
124
|
...queryPaging
|
|
143
125
|
});
|
|
144
126
|
// Reset items
|
|
145
|
-
if (
|
|
127
|
+
if (stateRefs.current.isMounted !== false)
|
|
146
128
|
setRows(items, true);
|
|
147
129
|
};
|
|
148
|
-
|
|
130
|
+
react_1.default.useImperativeHandle(mRef, () => ({
|
|
149
131
|
delete(index) {
|
|
150
132
|
const item = rows.at(index);
|
|
151
133
|
if (item) {
|
|
@@ -160,27 +142,28 @@ const ScrollerGrid = (props) => {
|
|
|
160
142
|
newRows.splice(start, 0, item);
|
|
161
143
|
setRows(newRows);
|
|
162
144
|
},
|
|
163
|
-
|
|
164
|
-
|
|
145
|
+
refresh() {
|
|
146
|
+
loadDataLocal(0);
|
|
165
147
|
},
|
|
166
|
-
|
|
167
|
-
|
|
148
|
+
reset,
|
|
149
|
+
scrollToCell(param) {
|
|
150
|
+
localRef.current?.scrollToCell(param);
|
|
168
151
|
},
|
|
169
|
-
|
|
170
|
-
|
|
152
|
+
scrollToColumn(param) {
|
|
153
|
+
localRef.current?.scrollToColumn(param);
|
|
171
154
|
},
|
|
172
|
-
|
|
173
|
-
|
|
155
|
+
scrollToRow(param) {
|
|
156
|
+
localRef.current?.scrollToRow(param);
|
|
174
157
|
},
|
|
175
158
|
select(rowIndex) {
|
|
176
159
|
// Select only one item
|
|
177
|
-
const selectedItems =
|
|
160
|
+
const selectedItems = stateRefs.current.selectedItems;
|
|
178
161
|
selectedItems[0] = rows[rowIndex];
|
|
179
162
|
if (onSelectChange)
|
|
180
163
|
onSelectChange(selectedItems);
|
|
181
164
|
},
|
|
182
165
|
selectAll(checked) {
|
|
183
|
-
const selectedItems =
|
|
166
|
+
const selectedItems = stateRefs.current.selectedItems;
|
|
184
167
|
rows.forEach((row) => {
|
|
185
168
|
const index = selectedItems.findIndex((selectedItem) => selectedItem[idField] === row[idField]);
|
|
186
169
|
if (checked) {
|
|
@@ -195,7 +178,7 @@ const ScrollerGrid = (props) => {
|
|
|
195
178
|
onSelectChange(selectedItems);
|
|
196
179
|
},
|
|
197
180
|
selectItem(item, checked) {
|
|
198
|
-
const selectedItems =
|
|
181
|
+
const selectedItems = stateRefs.current.selectedItems;
|
|
199
182
|
const index = selectedItems.findIndex((selectedItem) => selectedItem[idField] === item[idField]);
|
|
200
183
|
if (checked) {
|
|
201
184
|
if (index === -1)
|
|
@@ -207,35 +190,15 @@ const ScrollerGrid = (props) => {
|
|
|
207
190
|
}
|
|
208
191
|
if (onSelectChange)
|
|
209
192
|
onSelectChange(selectedItems);
|
|
210
|
-
},
|
|
211
|
-
reset,
|
|
212
|
-
resetAfterColumnIndex(index, shouldForceUpdate) {
|
|
213
|
-
ref.current?.resetAfterColumnIndex(index, shouldForceUpdate);
|
|
214
|
-
},
|
|
215
|
-
resetAfterIndices(params) {
|
|
216
|
-
ref.current?.resetAfterIndices(params);
|
|
217
|
-
},
|
|
218
|
-
resetAfterRowIndex(index, shouldForceUpdate) {
|
|
219
|
-
ref.current?.resetAfterRowIndex(index, shouldForceUpdate);
|
|
220
193
|
}
|
|
221
|
-
};
|
|
222
|
-
react_1.default.useImperativeHandle(mRef, () => instance, [rows]);
|
|
223
|
-
// Force update to work with the new width and rowHeight
|
|
224
|
-
react_1.default.useEffect(() => {
|
|
225
|
-
ref.current?.resetAfterIndices({
|
|
226
|
-
columnIndex: 0,
|
|
227
|
-
rowIndex: 0,
|
|
228
|
-
shouldForceUpdate: true
|
|
229
|
-
});
|
|
230
|
-
}, [width, rowHeight]);
|
|
194
|
+
}), [rows]);
|
|
231
195
|
// Rows
|
|
232
|
-
const
|
|
233
|
-
// Row count
|
|
234
|
-
const rowCount = refs.current.hasNextPage ? rowLength + 1 : rowLength;
|
|
196
|
+
const rowCount = rows.length;
|
|
235
197
|
react_1.default.useEffect(() => {
|
|
236
198
|
// Auto load data when current page is 0
|
|
237
|
-
if (
|
|
238
|
-
|
|
199
|
+
if (stateRefs.current.queryPaging.currentPage === 0 &&
|
|
200
|
+
stateRefs.current.autoLoad) {
|
|
201
|
+
const initItems = onInitLoad == null ? undefined : onInitLoad(stateRefs.current);
|
|
239
202
|
if (initItems)
|
|
240
203
|
reset(initItems[1], initItems[0]);
|
|
241
204
|
else
|
|
@@ -244,15 +207,18 @@ const ScrollerGrid = (props) => {
|
|
|
244
207
|
}, [onInitLoad, loadDataLocal]);
|
|
245
208
|
react_1.default.useEffect(() => {
|
|
246
209
|
return () => {
|
|
247
|
-
|
|
210
|
+
stateRefs.current.isMounted = false;
|
|
248
211
|
};
|
|
249
212
|
}, []);
|
|
250
213
|
// Layout
|
|
251
|
-
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [headerRenderer && headerRenderer(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
214
|
+
return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [headerRenderer && headerRenderer(stateRefs.current), (0, jsx_runtime_1.jsx)(react_window_1.Grid, { cellProps: { rows, states: stateRefs.current }, gridRef: refs, onCellsRendered: (visibleCells, allCells) => {
|
|
215
|
+
// No items, means no necessary to load more data during reset
|
|
216
|
+
if (rowCount > 0 &&
|
|
217
|
+
visibleCells.rowStopIndex + threshold > rowCount) {
|
|
218
|
+
// Auto load next page
|
|
219
|
+
loadDataLocal();
|
|
220
|
+
}
|
|
221
|
+
onCellsRendered?.(visibleCells, allCells);
|
|
222
|
+
}, overscanCount: threshold, rowHeight: rowHeight, rowCount: rowCount, style: style, ...rest }), footerRenderer && footerRenderer(rows, stateRefs.current)] }));
|
|
257
223
|
};
|
|
258
224
|
exports.ScrollerGrid = ScrollerGrid;
|