@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.
@@ -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 itemSize
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 { Align } from "react-window";
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 specified offset (scrollTop or scrollLeft, depending on the direction prop).
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
- scrollToItemRef(index: number, align?: Align): void;
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 { Align, GridChildComponentProps, VariableSizeGridProps } from "react-window";
3
+ import { GridProps } from "react-window";
4
4
  import { GridJsonData, GridLoadDataProps, GridLoader, GridLoaderStates } from "./GridLoader";
5
- import { GridMethodRef } from "./GridMethodRef";
6
- export type ScrollerGridItemRendererProps<T> = Omit<GridChildComponentProps<T>, "data"> & {
7
- /**
8
- * Selected items
9
- */
10
- selectedItems: T[];
11
- /**
12
- * Set items for rerenderer
13
- * @param callback Callback
14
- */
15
- setItems: (callback: (items: T[], ref: ScrollerGridForwardRef<T>) => T[] | undefined | void) => void;
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<VariableSizeGridProps<T>, "children" | "rowCount" | "rowHeight"> & {
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
- * Id field
30
+ * Height of the grid
35
31
  */
36
- idField?: DataTypes.Keys<T>;
32
+ height?: number | string;
37
33
  /**
38
- * Item renderer
34
+ * Id field
39
35
  */
40
- itemRenderer: (props: ScrollerGridItemRendererProps<T>) => React.ReactElement;
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
- * Returns the height of the specified row.
46
+ * Width of the grid
51
47
  */
52
- rowHeight?: ((index: number) => number) | number;
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 specified offsets
55
+ * Scroll to the cell
56
+ * @param param Parameters to control
60
57
  */
61
- scrollTo(params: {
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 specified item
60
+ * Scroll to the cell
61
+ * @param param Parameters to control
72
62
  */
73
- scrollToItem(params: {
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, itemRenderer, idField = "id", loadBatchSize, loadData, mRef, onItemsRendered, onSelectChange, rowHeight = 53, threshold = 6, width, onInitLoad, onUpdateRows, ...rest } = props;
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
- refs.current.loadedItems = rows.length;
31
+ stateRefs.current.loadedItems = rows.length;
22
32
  updateRows(rows);
23
33
  if (!reset && onUpdateRows)
24
- onUpdateRows(rows, refs.current);
34
+ onUpdateRows(rows, stateRefs.current);
25
35
  };
26
- // Refs
27
- const refs = react_1.default.useRef({
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 (!refs.current.hasNextPage ||
45
- refs.current.isNextPageLoading ||
46
- refs.current.isMounted === false)
53
+ if (!stateRefs.current.hasNextPage ||
54
+ stateRefs.current.isNextPageLoading ||
55
+ stateRefs.current.isMounted === false)
47
56
  return;
48
57
  // Update state
49
- refs.current.isNextPageLoading = true;
58
+ stateRefs.current.isNextPageLoading = true;
50
59
  // Parameters
51
- const { queryPaging, data } = refs.current;
60
+ const { queryPaging, data } = stateRefs.current;
52
61
  const loadProps = {
53
62
  queryPaging,
54
63
  data
55
64
  };
56
- loadData(loadProps, refs.current.lastItem).then((result) => {
57
- if (result == null || refs.current.isMounted === false) {
65
+ loadData(loadProps, stateRefs.current.lastItem).then((result) => {
66
+ if (result == null || stateRefs.current.isMounted === false) {
58
67
  return;
59
68
  }
60
- refs.current.isMounted = true;
69
+ stateRefs.current.isMounted = true;
61
70
  const newItems = result.length;
62
- refs.current.lastLoadedItems = newItems;
63
- refs.current.lastItem = result.at(-1);
64
- refs.current.isNextPageLoading = false;
65
- refs.current.hasNextPage = newItems >= refs.current.queryPaging.batchSize;
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 = refs.current.lastLoadedItems
78
+ const newRows = stateRefs.current.lastLoadedItems
69
79
  ? [...rows]
70
- .splice(rows.length - refs.current.lastLoadedItems, refs.current.lastLoadedItems)
80
+ .splice(rows.length - stateRefs.current.lastLoadedItems, stateRefs.current.lastLoadedItems)
71
81
  .concat(result)
72
82
  : result;
73
- refs.current.idCache = {};
83
+ stateRefs.current.idCache = {};
74
84
  for (const row of newRows) {
75
85
  const id = row[idField];
76
- refs.current.idCache[id] = null;
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 (refs.current.queryPaging.currentPage == null)
84
- refs.current.queryPaging.currentPage = pageAdd;
93
+ if (stateRefs.current.queryPaging.currentPage == null)
94
+ stateRefs.current.queryPaging.currentPage = pageAdd;
85
95
  else
86
- refs.current.queryPaging.currentPage += pageAdd;
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 (refs.current.idCache[id] === undefined) {
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(refs.current, resetState);
140
- Object.assign(refs.current.queryPaging, {
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 (refs.current.isMounted !== false)
127
+ if (stateRefs.current.isMounted !== false)
146
128
  setRows(items, true);
147
129
  };
148
- const instance = {
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
- scrollTo(params) {
164
- ref.current?.scrollTo(params);
145
+ refresh() {
146
+ loadDataLocal(0);
165
147
  },
166
- scrollToItem(params) {
167
- ref.current?.scrollToItem(params);
148
+ reset,
149
+ scrollToCell(param) {
150
+ localRef.current?.scrollToCell(param);
168
151
  },
169
- scrollToRef(scrollOffset) {
170
- ref.current?.scrollTo({ scrollLeft: 0, scrollTop: scrollOffset });
152
+ scrollToColumn(param) {
153
+ localRef.current?.scrollToColumn(param);
171
154
  },
172
- scrollToItemRef(index, align) {
173
- ref.current?.scrollToItem({ rowIndex: index, align });
155
+ scrollToRow(param) {
156
+ localRef.current?.scrollToRow(param);
174
157
  },
175
158
  select(rowIndex) {
176
159
  // Select only one item
177
- const selectedItems = refs.current.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 = refs.current.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 = refs.current.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 rowLength = rows.length;
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 (refs.current.queryPaging.currentPage === 0 && refs.current.autoLoad) {
238
- const initItems = onInitLoad == null ? undefined : onInitLoad(ref.current);
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
- refs.current.isMounted = false;
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(refs.current), (0, jsx_runtime_1.jsx)(react_window_1.VariableSizeGrid, { itemKey: ({ columnIndex, rowIndex, data }) => {
252
- if (data == null)
253
- return [rowIndex, columnIndex].join(",");
254
- // ${data[idField]}-${rowIndex} always unique but no cache for the same item
255
- return [`${data[idField]}`, columnIndex].join(",");
256
- }, onItemsRendered: onItemsRenderedLocal, ref: ref, rowCount: rowCount, rowHeight: typeof rowHeight === "function" ? rowHeight : () => rowHeight, style: { overflowX: "hidden" }, width: width, ...rest, children: (props) => itemRendererLocal(props, refs.current) }), footerRenderer && footerRenderer(rows, refs.current)] }));
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;