@etsoo/materialui 1.1.91 → 1.1.93

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/DataGridEx.js CHANGED
@@ -218,7 +218,7 @@ export function DataGridEx(props) {
218
218
  /**
219
219
  * Item renderer
220
220
  */
221
- const itemRenderer = ({ columnIndex, rowIndex, style, data, selectedItems }) => {
221
+ const itemRenderer = ({ columnIndex, rowIndex, style, data, selectedItems, setItems }) => {
222
222
  // Column
223
223
  const { align, cellRenderer = DataGridRenderers.defaultCellRenderer, cellBoxStyle, field, type, valueFormatter, renderProps } = columns[columnIndex];
224
224
  // Props
@@ -261,7 +261,8 @@ export function DataGridEx(props) {
261
261
  rowIndex,
262
262
  columnIndex,
263
263
  cellProps,
264
- renderProps
264
+ renderProps,
265
+ setItems
265
266
  });
266
267
  return (React.createElement("div", { className: rowClass, style: style, "data-row": rowIndex, "data-column": columnIndex, onMouseDown: selectable && !checkable ? handleMouseDown : undefined, onMouseOver: selectable ? handleMouseOver : undefined, onMouseOut: selectable ? handleMouseOut : undefined, onClick: (event) => onClick && data != null && onClick(event, data), onDoubleClick: (event) => onDoubleClick && data != null && onDoubleClick(event, data) },
267
268
  React.createElement(Box, { ...cellProps, onMouseEnter: handleMouseEnter }, child)));
package/lib/TableEx.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { GridColumn, GridLoader } from '@etsoo/react';
2
- import { GridMethodRef } from '@etsoo/react/lib/components/GridMethodRef';
3
- import { DataTypes, IdDefaultType } from '@etsoo/shared';
4
- import { TableProps } from '@mui/material';
5
- import React from 'react';
1
+ import { GridColumn, GridLoader } from "@etsoo/react";
2
+ import { GridMethodRef } from "@etsoo/react/lib/components/GridMethodRef";
3
+ import { DataTypes, IdDefaultType } from "@etsoo/shared";
4
+ import { TableProps } from "@mui/material";
5
+ import React from "react";
6
6
  /**
7
7
  * Extended table min width for width-unset column
8
8
  */
package/lib/TableEx.js CHANGED
@@ -1,8 +1,8 @@
1
- import { GridAlignGet, GridSizeGet } from '@etsoo/react';
2
- import { DataTypes } from '@etsoo/shared';
3
- import { Checkbox, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TableSortLabel, useTheme } from '@mui/material';
4
- import React from 'react';
5
- import { DataGridRenderers } from './DataGridRenderers';
1
+ import { GridAlignGet, GridSizeGet } from "@etsoo/react";
2
+ import { DataTypes } from "@etsoo/shared";
3
+ import { Checkbox, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TableSortLabel, useTheme } from "@mui/material";
4
+ import React from "react";
5
+ import { DataGridRenderers } from "./DataGridRenderers";
6
6
  /**
7
7
  * Extended table min width for width-unset column
8
8
  */
@@ -17,7 +17,7 @@ export function TableEx(props) {
17
17
  // Theme
18
18
  const theme = useTheme();
19
19
  // Destruct
20
- const { alternatingColors = [theme.palette.action.hover, undefined], autoLoad = true, columns, defaultOrderBy, headerColors = [undefined, undefined], idField = 'id', loadBatchSize, loadData, maxHeight, mRef, onSelectChange, rowHeight = 53, otherHeight = 110, threshold, ...rest } = props;
20
+ const { alternatingColors = [theme.palette.action.hover, undefined], autoLoad = true, columns, defaultOrderBy, headerColors = [undefined, undefined], idField = "id", loadBatchSize, loadData, maxHeight, mRef, onSelectChange, rowHeight = 53, otherHeight = 110, threshold, ...rest } = props;
21
21
  const selectable = onSelectChange != null;
22
22
  // Rows per page
23
23
  let rowsPerPageLocal;
@@ -27,7 +27,7 @@ export function TableEx(props) {
27
27
  else
28
28
  rowsPerPageLocal = Math.floor((maxHeight - otherHeight) / rowHeight);
29
29
  }
30
- else if (typeof loadBatchSize === 'number') {
30
+ else if (typeof loadBatchSize === "number") {
31
31
  rowsPerPageLocal = loadBatchSize;
32
32
  }
33
33
  else {
@@ -153,6 +153,13 @@ export function TableEx(props) {
153
153
  const handleSort = (field, asc) => {
154
154
  reset({ orderBy: field, orderByAsc: asc });
155
155
  };
156
+ // Set items for rerenderer
157
+ const setItems = (callback) => {
158
+ const result = callback(rows);
159
+ if (result == null)
160
+ return;
161
+ setRows(result);
162
+ };
156
163
  // Destruct states
157
164
  const { autoLoad: stateAutoLoad, currentPage, hasNextPage, lastLoadedItems, orderBy, batchSize, selectedItems } = state;
158
165
  // Current page selected items
@@ -181,14 +188,13 @@ export function TableEx(props) {
181
188
  React.createElement(Table, { ...rest },
182
189
  React.createElement(TableHead, null,
183
190
  React.createElement(TableRow, { sx: {
184
- '& th': {
191
+ "& th": {
185
192
  backgroundColor: headerColors[0],
186
193
  color: headerColors[1]
187
194
  }
188
195
  } },
189
196
  selectable && (React.createElement(TableCell, { padding: "checkbox" },
190
- React.createElement(Checkbox, { color: "primary", indeterminate: pageSelectedItems > 0 &&
191
- pageSelectedItems < rows.length, checked: pageSelectedItems > 0, onChange: (_event, checked) => handleSelectAll(checked) }))),
197
+ React.createElement(Checkbox, { color: "primary", indeterminate: pageSelectedItems > 0 && pageSelectedItems < rows.length, checked: pageSelectedItems > 0, onChange: (_event, checked) => handleSelectAll(checked) }))),
192
198
  columns.map((column, index) => {
193
199
  // Destruct
194
200
  const { align, field, header, minWidth, sortable, sortAsc = true, type, width } = column;
@@ -198,7 +204,7 @@ export function TableEx(props) {
198
204
  let sortLabel;
199
205
  if (sortable && field != null) {
200
206
  const active = orderBy === field;
201
- sortLabel = (React.createElement(TableSortLabel, { active: active, direction: sortAsc ? 'asc' : 'desc', onClick: (_event) => {
207
+ sortLabel = (React.createElement(TableSortLabel, { active: active, direction: sortAsc ? "asc" : "desc", onClick: (_event) => {
202
208
  if (active)
203
209
  column.sortAsc = !sortAsc;
204
210
  handleSort(field, column.sortAsc);
@@ -216,18 +222,16 @@ export function TableEx(props) {
216
222
  } }, sortLabel));
217
223
  }))),
218
224
  React.createElement(TableBody, { sx: {
219
- '& tr:nth-of-type(odd):not(.Mui-selected)': {
225
+ "& tr:nth-of-type(odd):not(.Mui-selected)": {
220
226
  backgroundColor: alternatingColors[0]
221
227
  },
222
- '& tr:nth-of-type(even):not(.Mui-selected)': {
228
+ "& tr:nth-of-type(even):not(.Mui-selected)": {
223
229
  backgroundColor: alternatingColors[1]
224
230
  }
225
231
  } }, [...Array(batchSize)].map((_item, rowIndex) => {
226
232
  var _a;
227
233
  // Row
228
- const row = rowIndex < rows.length
229
- ? rows[rowIndex]
230
- : undefined;
234
+ const row = rowIndex < rows.length ? rows[rowIndex] : undefined;
231
235
  // Row id field value
232
236
  const rowId = (_a = DataTypes.getValue(row, idField)) !== null && _a !== void 0 ? _a : rowIndex;
233
237
  // Selected or not
@@ -245,7 +249,7 @@ export function TableEx(props) {
245
249
  };
246
250
  const cellProps = {
247
251
  align: GridAlignGet(align, type),
248
- valign: 'middle'
252
+ valign: "middle"
249
253
  };
250
254
  const child = row ? (cellRenderer({
251
255
  data: row,
@@ -257,7 +261,8 @@ export function TableEx(props) {
257
261
  type,
258
262
  rowIndex,
259
263
  columnIndex,
260
- cellProps
264
+ cellProps,
265
+ setItems
261
266
  })) : (React.createElement(React.Fragment, null, "\u00A0"));
262
267
  return (React.createElement(TableCell, { key: `${rowId}${columnIndex}`, ...cellProps }, child));
263
268
  })));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.1.91",
3
+ "version": "1.1.93",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -50,10 +50,10 @@
50
50
  "@emotion/css": "^11.10.6",
51
51
  "@emotion/react": "^11.10.6",
52
52
  "@emotion/styled": "^11.10.6",
53
- "@etsoo/appscript": "^1.3.88",
53
+ "@etsoo/appscript": "^1.3.89",
54
54
  "@etsoo/notificationbase": "^1.1.24",
55
- "@etsoo/react": "^1.6.58",
56
- "@etsoo/shared": "^1.1.96",
55
+ "@etsoo/react": "^1.6.61",
56
+ "@etsoo/shared": "^1.1.99",
57
57
  "@mui/icons-material": "^5.11.16",
58
58
  "@mui/material": "^5.11.16",
59
59
  "@mui/x-data-grid": "^6.0.4",
@@ -89,6 +89,6 @@
89
89
  "@typescript-eslint/parser": "^5.57.1",
90
90
  "jest": "^29.5.0",
91
91
  "jest-environment-jsdom": "^29.5.0",
92
- "typescript": "^5.0.3"
92
+ "typescript": "^5.0.4"
93
93
  }
94
94
  }
@@ -498,7 +498,8 @@ export function DataGridEx<
498
498
  rowIndex,
499
499
  style,
500
500
  data,
501
- selectedItems
501
+ selectedItems,
502
+ setItems
502
503
  }: ScrollerGridItemRendererProps<T>) => {
503
504
  // Column
504
505
  const {
@@ -564,7 +565,8 @@ export function DataGridEx<
564
565
  rowIndex,
565
566
  columnIndex,
566
567
  cellProps,
567
- renderProps
568
+ renderProps,
569
+ setItems
568
570
  });
569
571
 
570
572
  return (
package/src/TableEx.tsx CHANGED
@@ -1,31 +1,31 @@
1
1
  import {
2
- GridAlignGet,
3
- GridCellFormatterProps,
4
- GridColumn,
5
- GridLoadDataProps,
6
- GridLoader,
7
- GridLoaderStates,
8
- GridSizeGet
9
- } from '@etsoo/react';
10
- import { GridMethodRef } from '@etsoo/react/lib/components/GridMethodRef';
11
- import { DataTypes, IdDefaultType } from '@etsoo/shared';
2
+ GridAlignGet,
3
+ GridCellFormatterProps,
4
+ GridColumn,
5
+ GridLoadDataProps,
6
+ GridLoader,
7
+ GridLoaderStates,
8
+ GridSizeGet
9
+ } from "@etsoo/react";
10
+ import { GridMethodRef } from "@etsoo/react/lib/components/GridMethodRef";
11
+ import { DataTypes, IdDefaultType } from "@etsoo/shared";
12
12
  import {
13
- Checkbox,
14
- Paper,
15
- Table,
16
- TableBody,
17
- TableCell,
18
- TableCellProps,
19
- TableContainer,
20
- TableHead,
21
- TablePagination,
22
- TableProps,
23
- TableRow,
24
- TableSortLabel,
25
- useTheme
26
- } from '@mui/material';
27
- import React from 'react';
28
- import { DataGridRenderers } from './DataGridRenderers';
13
+ Checkbox,
14
+ Paper,
15
+ Table,
16
+ TableBody,
17
+ TableCell,
18
+ TableCellProps,
19
+ TableContainer,
20
+ TableHead,
21
+ TablePagination,
22
+ TableProps,
23
+ TableRow,
24
+ TableSortLabel,
25
+ useTheme
26
+ } from "@mui/material";
27
+ import React from "react";
28
+ import { DataGridRenderers } from "./DataGridRenderers";
29
29
 
30
30
  /**
31
31
  * Extended table min width for width-unset column
@@ -36,65 +36,65 @@ export const TableExMinWidth: number = 180;
36
36
  * Extended table methods ref
37
37
  */
38
38
  export interface TableExMethodRef<T> extends GridMethodRef<T> {
39
- /**
40
- * Refresh data
41
- */
42
- refresh(): void;
39
+ /**
40
+ * Refresh data
41
+ */
42
+ refresh(): void;
43
43
  }
44
44
 
45
45
  /**
46
46
  * Extended table props
47
47
  */
48
48
  export type TableExProps<
49
- T extends object,
50
- D extends DataTypes.Keys<T>
49
+ T extends object,
50
+ D extends DataTypes.Keys<T>
51
51
  > = TableProps &
52
- GridLoader<T> & {
53
- /**
54
- * Alternating colors for odd/even rows
55
- */
56
- alternatingColors?: [string?, string?];
57
-
58
- /**
59
- * Columns
60
- */
61
- columns: GridColumn<T>[];
62
-
63
- /**
64
- * Header cells background color and font color
65
- */
66
- headerColors?: [string?, string?];
67
-
68
- /**
69
- * Id field
70
- */
71
- idField?: D;
72
-
73
- /**
74
- * Max height
75
- */
76
- maxHeight?: number;
77
-
78
- /**
79
- * Methods
80
- */
81
- mRef?: React.Ref<TableExMethodRef<T>>;
82
-
83
- /**
84
- * On items select change
85
- */
86
- onSelectChange?: (selectedItems: T[]) => void;
87
-
88
- /**
89
- * Row height
90
- */
91
- rowHeight?: number;
92
-
93
- /**
94
- * Header and bottom height
95
- */
96
- otherHeight?: number;
97
- };
52
+ GridLoader<T> & {
53
+ /**
54
+ * Alternating colors for odd/even rows
55
+ */
56
+ alternatingColors?: [string?, string?];
57
+
58
+ /**
59
+ * Columns
60
+ */
61
+ columns: GridColumn<T>[];
62
+
63
+ /**
64
+ * Header cells background color and font color
65
+ */
66
+ headerColors?: [string?, string?];
67
+
68
+ /**
69
+ * Id field
70
+ */
71
+ idField?: D;
72
+
73
+ /**
74
+ * Max height
75
+ */
76
+ maxHeight?: number;
77
+
78
+ /**
79
+ * Methods
80
+ */
81
+ mRef?: React.Ref<TableExMethodRef<T>>;
82
+
83
+ /**
84
+ * On items select change
85
+ */
86
+ onSelectChange?: (selectedItems: T[]) => void;
87
+
88
+ /**
89
+ * Row height
90
+ */
91
+ rowHeight?: number;
92
+
93
+ /**
94
+ * Header and bottom height
95
+ */
96
+ otherHeight?: number;
97
+ };
98
98
 
99
99
  /**
100
100
  * Extended Table
@@ -102,458 +102,433 @@ export type TableExProps<
102
102
  * @returns Component
103
103
  */
104
104
  export function TableEx<
105
- T extends object,
106
- D extends DataTypes.Keys<T> = IdDefaultType<T>
105
+ T extends object,
106
+ D extends DataTypes.Keys<T> = IdDefaultType<T>
107
107
  >(props: TableExProps<T, D>) {
108
- // Theme
109
- const theme = useTheme();
110
-
111
- // Destruct
112
- const {
113
- alternatingColors = [theme.palette.action.hover, undefined],
114
- autoLoad = true,
115
- columns,
116
- defaultOrderBy,
117
- headerColors = [undefined, undefined],
118
- idField = 'id' as D,
119
- loadBatchSize,
120
- loadData,
121
- maxHeight,
122
- mRef,
123
- onSelectChange,
124
- rowHeight = 53,
125
- otherHeight = 110,
126
- threshold,
127
- ...rest
128
- } = props;
129
-
130
- const selectable: boolean = onSelectChange != null;
131
-
132
- // Rows per page
133
- let rowsPerPageLocal: number;
134
- if (maxHeight != null) {
135
- if (loadBatchSize != null)
136
- rowsPerPageLocal = GridSizeGet(loadBatchSize, maxHeight);
137
- else
138
- rowsPerPageLocal = Math.floor(
139
- (maxHeight - otherHeight) / rowHeight
140
- );
141
- } else if (typeof loadBatchSize === 'number') {
142
- rowsPerPageLocal = loadBatchSize;
143
- } else {
144
- rowsPerPageLocal = 10;
145
- }
146
-
147
- // Rows
148
- const [rows, updateRows] = React.useState<T[]>([]);
149
- const setRows = (rows: T[]) => {
150
- state.loadedItems = rows.length;
151
- updateRows(rows);
152
- };
153
-
154
- // States
155
- const stateRefs = React.useRef<GridLoaderStates<T>>({
156
- autoLoad,
157
- currentPage: 0,
158
- loadedItems: 0,
159
- hasNextPage: true,
160
- isNextPageLoading: false,
161
- orderBy: defaultOrderBy,
162
- orderByAsc: defaultOrderBy
163
- ? columns.find((column) => column.field === defaultOrderBy)?.sortAsc
164
- : undefined,
165
- batchSize: rowsPerPageLocal,
166
- selectedItems: [],
167
- idCache: {}
168
- });
169
- const state = stateRefs.current;
170
-
171
- // Reset the state and load again
172
- const reset = (add?: Partial<GridLoaderStates<T>>) => {
173
- const resetState: Partial<GridLoaderStates<T>> = {
174
- autoLoad: true,
175
- currentPage: 0,
176
- loadedItems: 0,
177
- hasNextPage: true,
178
- isNextPageLoading: false,
179
- lastLoadedItems: undefined,
180
- ...add
181
- };
182
- Object.assign(state, resetState);
183
- };
184
-
185
- React.useImperativeHandle(
186
- mRef,
187
- () => ({
188
- /**
189
- * Refresh data
190
- */
191
- refresh(): void {
192
- loadDataLocal();
193
- },
194
-
195
- /**
196
- * Reset
197
- */
198
- reset
199
- }),
200
- []
201
- );
202
-
203
- // Load data
204
- const loadDataLocal = () => {
205
- // Prevent multiple loadings
206
- if (!state.hasNextPage || state.isNextPageLoading) return;
207
-
208
- // Update state
209
- state.isNextPageLoading = true;
210
-
211
- // Parameters
212
- const { currentPage, batchSize, orderBy, orderByAsc, data, isMounted } =
213
- state;
214
-
215
- const loadProps: GridLoadDataProps = {
216
- currentPage,
217
- batchSize,
218
- orderBy,
219
- orderByAsc,
220
- data
221
- };
222
-
223
- loadData(loadProps).then((result) => {
224
- state.isMounted = true;
225
- if (result == null || isMounted === false) {
226
- return;
227
- }
228
-
229
- const newItems = result.length;
230
- state.lastLoadedItems = newItems;
231
- state.hasNextPage = newItems >= batchSize;
232
- state.isNextPageLoading = false;
233
-
234
- // Update rows
235
- setRows(result);
236
- });
108
+ // Theme
109
+ const theme = useTheme();
110
+
111
+ // Destruct
112
+ const {
113
+ alternatingColors = [theme.palette.action.hover, undefined],
114
+ autoLoad = true,
115
+ columns,
116
+ defaultOrderBy,
117
+ headerColors = [undefined, undefined],
118
+ idField = "id" as D,
119
+ loadBatchSize,
120
+ loadData,
121
+ maxHeight,
122
+ mRef,
123
+ onSelectChange,
124
+ rowHeight = 53,
125
+ otherHeight = 110,
126
+ threshold,
127
+ ...rest
128
+ } = props;
129
+
130
+ const selectable: boolean = onSelectChange != null;
131
+
132
+ // Rows per page
133
+ let rowsPerPageLocal: number;
134
+ if (maxHeight != null) {
135
+ if (loadBatchSize != null)
136
+ rowsPerPageLocal = GridSizeGet(loadBatchSize, maxHeight);
137
+ else rowsPerPageLocal = Math.floor((maxHeight - otherHeight) / rowHeight);
138
+ } else if (typeof loadBatchSize === "number") {
139
+ rowsPerPageLocal = loadBatchSize;
140
+ } else {
141
+ rowsPerPageLocal = 10;
142
+ }
143
+
144
+ // Rows
145
+ const [rows, updateRows] = React.useState<T[]>([]);
146
+ const setRows = (rows: T[]) => {
147
+ state.loadedItems = rows.length;
148
+ updateRows(rows);
149
+ };
150
+
151
+ // States
152
+ const stateRefs = React.useRef<GridLoaderStates<T>>({
153
+ autoLoad,
154
+ currentPage: 0,
155
+ loadedItems: 0,
156
+ hasNextPage: true,
157
+ isNextPageLoading: false,
158
+ orderBy: defaultOrderBy,
159
+ orderByAsc: defaultOrderBy
160
+ ? columns.find((column) => column.field === defaultOrderBy)?.sortAsc
161
+ : undefined,
162
+ batchSize: rowsPerPageLocal,
163
+ selectedItems: [],
164
+ idCache: {}
165
+ });
166
+ const state = stateRefs.current;
167
+
168
+ // Reset the state and load again
169
+ const reset = (add?: Partial<GridLoaderStates<T>>) => {
170
+ const resetState: Partial<GridLoaderStates<T>> = {
171
+ autoLoad: true,
172
+ currentPage: 0,
173
+ loadedItems: 0,
174
+ hasNextPage: true,
175
+ isNextPageLoading: false,
176
+ lastLoadedItems: undefined,
177
+ ...add
237
178
  };
238
-
239
- const handleChangePage = (_event: unknown, newPage: number) => {
240
- state.hasNextPage = true;
241
- state.currentPage = newPage;
179
+ Object.assign(state, resetState);
180
+ };
181
+
182
+ React.useImperativeHandle(
183
+ mRef,
184
+ () => ({
185
+ /**
186
+ * Refresh data
187
+ */
188
+ refresh(): void {
242
189
  loadDataLocal();
190
+ },
191
+
192
+ /**
193
+ * Reset
194
+ */
195
+ reset
196
+ }),
197
+ []
198
+ );
199
+
200
+ // Load data
201
+ const loadDataLocal = () => {
202
+ // Prevent multiple loadings
203
+ if (!state.hasNextPage || state.isNextPageLoading) return;
204
+
205
+ // Update state
206
+ state.isNextPageLoading = true;
207
+
208
+ // Parameters
209
+ const { currentPage, batchSize, orderBy, orderByAsc, data, isMounted } =
210
+ state;
211
+
212
+ const loadProps: GridLoadDataProps = {
213
+ currentPage,
214
+ batchSize,
215
+ orderBy,
216
+ orderByAsc,
217
+ data
243
218
  };
244
219
 
245
- const handleChangeRowsPerPage = (
246
- event: React.ChangeEvent<HTMLInputElement>
247
- ) => {
248
- const batchSize = parseInt(event.target.value);
249
- reset({ batchSize });
250
- };
251
-
252
- const handleSelect = (item: T, checked: Boolean) => {
253
- const selectedItems = state.selectedItems;
254
-
255
- const index = selectedItems.findIndex(
256
- (selectedItem) => selectedItem[idField] === item[idField]
257
- );
258
- if (checked) {
259
- if (index === -1) selectedItems.push(item);
260
- } else {
261
- if (index !== -1) selectedItems.splice(index, 1);
262
- }
263
-
264
- if (onSelectChange != null) {
265
- onSelectChange(selectedItems);
266
- }
267
- };
220
+ loadData(loadProps).then((result) => {
221
+ state.isMounted = true;
222
+ if (result == null || isMounted === false) {
223
+ return;
224
+ }
268
225
 
269
- const handleSelectAll = (checked: boolean) => {
270
- const selectedItems = state.selectedItems;
226
+ const newItems = result.length;
227
+ state.lastLoadedItems = newItems;
228
+ state.hasNextPage = newItems >= batchSize;
229
+ state.isNextPageLoading = false;
271
230
 
272
- rows.forEach((row) => {
273
- const index = selectedItems.findIndex(
274
- (selectedItem) => selectedItem[idField] === row[idField]
275
- );
231
+ // Update rows
232
+ setRows(result);
233
+ });
234
+ };
235
+
236
+ const handleChangePage = (_event: unknown, newPage: number) => {
237
+ state.hasNextPage = true;
238
+ state.currentPage = newPage;
239
+ loadDataLocal();
240
+ };
241
+
242
+ const handleChangeRowsPerPage = (
243
+ event: React.ChangeEvent<HTMLInputElement>
244
+ ) => {
245
+ const batchSize = parseInt(event.target.value);
246
+ reset({ batchSize });
247
+ };
248
+
249
+ const handleSelect = (item: T, checked: Boolean) => {
250
+ const selectedItems = state.selectedItems;
251
+
252
+ const index = selectedItems.findIndex(
253
+ (selectedItem) => selectedItem[idField] === item[idField]
254
+ );
255
+ if (checked) {
256
+ if (index === -1) selectedItems.push(item);
257
+ } else {
258
+ if (index !== -1) selectedItems.splice(index, 1);
259
+ }
276
260
 
277
- if (checked) {
278
- if (index === -1) selectedItems.push(row);
279
- } else if (index !== -1) {
280
- selectedItems.splice(index, 1);
281
- }
282
- });
261
+ if (onSelectChange != null) {
262
+ onSelectChange(selectedItems);
263
+ }
264
+ };
283
265
 
284
- if (onSelectChange != null) {
285
- onSelectChange(selectedItems);
286
- }
287
- };
266
+ const handleSelectAll = (checked: boolean) => {
267
+ const selectedItems = state.selectedItems;
288
268
 
289
- // New sort
290
- const handleSort = (field: string, asc?: boolean) => {
291
- reset({ orderBy: field, orderByAsc: asc });
292
- };
269
+ rows.forEach((row) => {
270
+ const index = selectedItems.findIndex(
271
+ (selectedItem) => selectedItem[idField] === row[idField]
272
+ );
293
273
 
294
- // Destruct states
295
- const {
296
- autoLoad: stateAutoLoad,
297
- currentPage,
298
- hasNextPage,
299
- lastLoadedItems,
300
- orderBy,
301
- batchSize,
302
- selectedItems
303
- } = state;
304
-
305
- // Current page selected items
306
- const pageSelectedItems = selectable
307
- ? rows.reduce((previousValue, currentItem) => {
308
- if (
309
- selectedItems.some(
310
- (item) => item[idField] === currentItem[idField]
311
- )
312
- )
313
- return previousValue + 1;
314
-
315
- return previousValue;
316
- }, 0)
317
- : 0;
318
-
319
- // Total rows
320
- const totalRows = hasNextPage
321
- ? -1
322
- : currentPage * batchSize + (lastLoadedItems ?? 0);
323
-
324
- // Auto load data when current page is 0
325
- if (currentPage === 0 && stateAutoLoad && lastLoadedItems == null)
326
- loadDataLocal();
274
+ if (checked) {
275
+ if (index === -1) selectedItems.push(row);
276
+ } else if (index !== -1) {
277
+ selectedItems.splice(index, 1);
278
+ }
279
+ });
327
280
 
328
- React.useEffect(() => {
329
- return () => {
330
- state.isMounted = false;
331
- };
332
- }, []);
333
-
334
- // Layout
335
- return (
336
- <Paper>
337
- <TableContainer sx={{ maxHeight }}>
338
- <Table {...rest}>
339
- <TableHead>
340
- <TableRow
341
- sx={{
342
- '& th': {
343
- backgroundColor: headerColors[0],
344
- color: headerColors[1]
345
- }
346
- }}
347
- >
348
- {selectable && (
349
- <TableCell padding="checkbox">
350
- <Checkbox
351
- color="primary"
352
- indeterminate={
353
- pageSelectedItems > 0 &&
354
- pageSelectedItems < rows.length
355
- }
356
- checked={pageSelectedItems > 0}
357
- onChange={(_event, checked) =>
358
- handleSelectAll(checked)
359
- }
360
- />
361
- </TableCell>
362
- )}
363
- {columns.map((column, index) => {
364
- // Destruct
365
- const {
366
- align,
367
- field,
368
- header,
369
- minWidth,
370
- sortable,
371
- sortAsc = true,
372
- type,
373
- width
374
- } = column;
375
-
376
- // Header text
377
- const headerText = header ?? field;
378
-
379
- // Sortable
380
- let sortLabel: React.ReactNode;
381
- if (sortable && field != null) {
382
- const active = orderBy === field;
383
-
384
- sortLabel = (
385
- <TableSortLabel
386
- active={active}
387
- direction={sortAsc ? 'asc' : 'desc'}
388
- onClick={(_event) => {
389
- if (active)
390
- column.sortAsc = !sortAsc;
391
-
392
- handleSort(
393
- field,
394
- column.sortAsc
395
- );
396
- }}
397
- >
398
- {headerText}
399
- </TableSortLabel>
400
- );
401
- } else {
402
- sortLabel = headerText;
403
- }
404
-
405
- return (
406
- <TableCell
407
- align={GridAlignGet(align, type)}
408
- key={field ?? index.toString()}
409
- width={width}
410
- sx={{
411
- minWidth:
412
- minWidth == null
413
- ? width == null
414
- ? TableExMinWidth
415
- : undefined
416
- : minWidth
417
- }}
418
- >
419
- {sortLabel}
420
- </TableCell>
421
- );
422
- })}
423
- </TableRow>
424
- </TableHead>
425
- <TableBody
426
- sx={{
427
- '& tr:nth-of-type(odd):not(.Mui-selected)': {
428
- backgroundColor: alternatingColors[0]
429
- },
430
- '& tr:nth-of-type(even):not(.Mui-selected)': {
431
- backgroundColor: alternatingColors[1]
432
- }
433
- }}
281
+ if (onSelectChange != null) {
282
+ onSelectChange(selectedItems);
283
+ }
284
+ };
285
+
286
+ // New sort
287
+ const handleSort = (field: string, asc?: boolean) => {
288
+ reset({ orderBy: field, orderByAsc: asc });
289
+ };
290
+
291
+ // Set items for rerenderer
292
+ const setItems = (callback: (items: T[]) => T[] | undefined) => {
293
+ const result = callback(rows);
294
+ if (result == null) return;
295
+ setRows(result);
296
+ };
297
+
298
+ // Destruct states
299
+ const {
300
+ autoLoad: stateAutoLoad,
301
+ currentPage,
302
+ hasNextPage,
303
+ lastLoadedItems,
304
+ orderBy,
305
+ batchSize,
306
+ selectedItems
307
+ } = state;
308
+
309
+ // Current page selected items
310
+ const pageSelectedItems = selectable
311
+ ? rows.reduce((previousValue, currentItem) => {
312
+ if (
313
+ selectedItems.some((item) => item[idField] === currentItem[idField])
314
+ )
315
+ return previousValue + 1;
316
+
317
+ return previousValue;
318
+ }, 0)
319
+ : 0;
320
+
321
+ // Total rows
322
+ const totalRows = hasNextPage
323
+ ? -1
324
+ : currentPage * batchSize + (lastLoadedItems ?? 0);
325
+
326
+ // Auto load data when current page is 0
327
+ if (currentPage === 0 && stateAutoLoad && lastLoadedItems == null)
328
+ loadDataLocal();
329
+
330
+ React.useEffect(() => {
331
+ return () => {
332
+ state.isMounted = false;
333
+ };
334
+ }, []);
335
+
336
+ // Layout
337
+ return (
338
+ <Paper>
339
+ <TableContainer sx={{ maxHeight }}>
340
+ <Table {...rest}>
341
+ <TableHead>
342
+ <TableRow
343
+ sx={{
344
+ "& th": {
345
+ backgroundColor: headerColors[0],
346
+ color: headerColors[1]
347
+ }
348
+ }}
349
+ >
350
+ {selectable && (
351
+ <TableCell padding="checkbox">
352
+ <Checkbox
353
+ color="primary"
354
+ indeterminate={
355
+ pageSelectedItems > 0 && pageSelectedItems < rows.length
356
+ }
357
+ checked={pageSelectedItems > 0}
358
+ onChange={(_event, checked) => handleSelectAll(checked)}
359
+ />
360
+ </TableCell>
361
+ )}
362
+ {columns.map((column, index) => {
363
+ // Destruct
364
+ const {
365
+ align,
366
+ field,
367
+ header,
368
+ minWidth,
369
+ sortable,
370
+ sortAsc = true,
371
+ type,
372
+ width
373
+ } = column;
374
+
375
+ // Header text
376
+ const headerText = header ?? field;
377
+
378
+ // Sortable
379
+ let sortLabel: React.ReactNode;
380
+ if (sortable && field != null) {
381
+ const active = orderBy === field;
382
+
383
+ sortLabel = (
384
+ <TableSortLabel
385
+ active={active}
386
+ direction={sortAsc ? "asc" : "desc"}
387
+ onClick={(_event) => {
388
+ if (active) column.sortAsc = !sortAsc;
389
+
390
+ handleSort(field, column.sortAsc);
391
+ }}
434
392
  >
435
- {[...Array(batchSize)].map((_item, rowIndex) => {
436
- // Row
437
- const row =
438
- rowIndex < rows.length
439
- ? rows[rowIndex]
440
- : undefined;
441
-
442
- // Row id field value
443
- const rowId =
444
- DataTypes.getValue(row, idField) ?? rowIndex;
445
-
446
- // Selected or not
447
- const isItemSelected = selectable
448
- ? selectedItems.some(
449
- (item) => item[idField] === rowId
450
- )
451
- : false;
452
-
453
- return (
454
- <TableRow
455
- key={rowId as unknown as React.Key}
456
- selected={isItemSelected}
457
- >
458
- {selectable && (
459
- <TableCell padding="checkbox">
460
- {row && (
461
- <Checkbox
462
- color="primary"
463
- checked={isItemSelected}
464
- onChange={(
465
- _event,
466
- checked
467
- ) =>
468
- handleSelect(
469
- row,
470
- checked
471
- )
472
- }
473
- />
474
- )}
475
- </TableCell>
476
- )}
477
- {columns.map(
478
- (
479
- {
480
- align,
481
- cellRenderer = DataGridRenderers.defaultCellRenderer,
482
- field,
483
- type,
484
- valueFormatter
485
- },
486
- columnIndex
487
- ) => {
488
- const formatProps: GridCellFormatterProps<T> =
489
- {
490
- data: row,
491
- field,
492
- rowIndex,
493
- columnIndex
494
- };
495
-
496
- const cellProps: TableCellProps = {
497
- align: GridAlignGet(
498
- align,
499
- type
500
- ),
501
- valign: 'middle'
502
- };
503
-
504
- const child = row ? (
505
- cellRenderer({
506
- data: row,
507
- field,
508
- formattedValue:
509
- valueFormatter
510
- ? valueFormatter(
511
- formatProps
512
- )
513
- : undefined,
514
- selected: isItemSelected,
515
- type,
516
- rowIndex,
517
- columnIndex,
518
- cellProps
519
- })
520
- ) : (
521
- <React.Fragment>
522
- &nbsp;
523
- </React.Fragment>
524
- );
525
-
526
- return (
527
- <TableCell
528
- key={`${rowId}${columnIndex}`}
529
- {...cellProps}
530
- >
531
- {child}
532
- </TableCell>
533
- );
534
- }
535
- )}
536
- </TableRow>
537
- );
538
- })}
539
- </TableBody>
540
- </Table>
541
- </TableContainer>
542
- <TablePagination
543
- component="div"
544
- showFirstButton
545
- count={totalRows}
546
- rowsPerPage={batchSize}
547
- page={currentPage}
548
- onPageChange={handleChangePage}
549
- onRowsPerPageChange={handleChangeRowsPerPage}
550
- rowsPerPageOptions={[
551
- batchSize,
552
- 2 * batchSize,
553
- 5 * batchSize,
554
- 10 * batchSize
555
- ]}
556
- />
557
- </Paper>
558
- );
393
+ {headerText}
394
+ </TableSortLabel>
395
+ );
396
+ } else {
397
+ sortLabel = headerText;
398
+ }
399
+
400
+ return (
401
+ <TableCell
402
+ align={GridAlignGet(align, type)}
403
+ key={field ?? index.toString()}
404
+ width={width}
405
+ sx={{
406
+ minWidth:
407
+ minWidth == null
408
+ ? width == null
409
+ ? TableExMinWidth
410
+ : undefined
411
+ : minWidth
412
+ }}
413
+ >
414
+ {sortLabel}
415
+ </TableCell>
416
+ );
417
+ })}
418
+ </TableRow>
419
+ </TableHead>
420
+ <TableBody
421
+ sx={{
422
+ "& tr:nth-of-type(odd):not(.Mui-selected)": {
423
+ backgroundColor: alternatingColors[0]
424
+ },
425
+ "& tr:nth-of-type(even):not(.Mui-selected)": {
426
+ backgroundColor: alternatingColors[1]
427
+ }
428
+ }}
429
+ >
430
+ {[...Array(batchSize)].map((_item, rowIndex) => {
431
+ // Row
432
+ const row = rowIndex < rows.length ? rows[rowIndex] : undefined;
433
+
434
+ // Row id field value
435
+ const rowId = DataTypes.getValue(row, idField) ?? rowIndex;
436
+
437
+ // Selected or not
438
+ const isItemSelected = selectable
439
+ ? selectedItems.some((item) => item[idField] === rowId)
440
+ : false;
441
+
442
+ return (
443
+ <TableRow
444
+ key={rowId as unknown as React.Key}
445
+ selected={isItemSelected}
446
+ >
447
+ {selectable && (
448
+ <TableCell padding="checkbox">
449
+ {row && (
450
+ <Checkbox
451
+ color="primary"
452
+ checked={isItemSelected}
453
+ onChange={(_event, checked) =>
454
+ handleSelect(row, checked)
455
+ }
456
+ />
457
+ )}
458
+ </TableCell>
459
+ )}
460
+ {columns.map(
461
+ (
462
+ {
463
+ align,
464
+ cellRenderer = DataGridRenderers.defaultCellRenderer,
465
+ field,
466
+ type,
467
+ valueFormatter
468
+ },
469
+ columnIndex
470
+ ) => {
471
+ const formatProps: GridCellFormatterProps<T> = {
472
+ data: row,
473
+ field,
474
+ rowIndex,
475
+ columnIndex
476
+ };
477
+
478
+ const cellProps: TableCellProps = {
479
+ align: GridAlignGet(align, type),
480
+ valign: "middle"
481
+ };
482
+
483
+ const child = row ? (
484
+ cellRenderer({
485
+ data: row,
486
+ field,
487
+ formattedValue: valueFormatter
488
+ ? valueFormatter(formatProps)
489
+ : undefined,
490
+ selected: isItemSelected,
491
+ type,
492
+ rowIndex,
493
+ columnIndex,
494
+ cellProps,
495
+ setItems
496
+ })
497
+ ) : (
498
+ <React.Fragment>&nbsp;</React.Fragment>
499
+ );
500
+
501
+ return (
502
+ <TableCell
503
+ key={`${rowId}${columnIndex}`}
504
+ {...cellProps}
505
+ >
506
+ {child}
507
+ </TableCell>
508
+ );
509
+ }
510
+ )}
511
+ </TableRow>
512
+ );
513
+ })}
514
+ </TableBody>
515
+ </Table>
516
+ </TableContainer>
517
+ <TablePagination
518
+ component="div"
519
+ showFirstButton
520
+ count={totalRows}
521
+ rowsPerPage={batchSize}
522
+ page={currentPage}
523
+ onPageChange={handleChangePage}
524
+ onRowsPerPageChange={handleChangeRowsPerPage}
525
+ rowsPerPageOptions={[
526
+ batchSize,
527
+ 2 * batchSize,
528
+ 5 * batchSize,
529
+ 10 * batchSize
530
+ ]}
531
+ />
532
+ </Paper>
533
+ );
559
534
  }