@etsoo/materialui 1.1.90 → 1.1.92

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/ItemList.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import React from "react";
2
- import { Dialog, DialogTitle, List, ListItemText, DialogContent, Button, ListItemButton, } from "@mui/material";
3
- import { DataTypes, } from "@etsoo/shared";
2
+ import { Dialog, DialogTitle, List, ListItemText, DialogContent, Button, ListItemButton } from "@mui/material";
3
+ import { DataTypes } from "@etsoo/shared";
4
4
  /**
5
5
  * Item list component
6
6
  * @param props Properties
7
7
  */
8
8
  export function ItemList(props) {
9
9
  // properties destructure
10
- const { buttonLabel, className, color = "primary", keepClick = false, items, idField = "id", labelField = "label", minWidth, icon, onClose, selectedValue, size = "medium", title, variant = "outlined", } = props;
10
+ const { buttonLabel, className, color = "primary", keepClick = false, items, idField = "id", labelField = "label", minWidth, icon, onClose, selectedValue, size = "medium", title, variant = "outlined" } = props;
11
11
  // Get label
12
12
  const getLabel = (item) => {
13
13
  var _a;
@@ -26,8 +26,8 @@ export function ItemList(props) {
26
26
  const [currentItem, setCurrentItem] = React.useState(defaultItem);
27
27
  // Click handler
28
28
  const clickHandler = () => {
29
- // More than one language
30
- if (items.length < 2) {
29
+ if (items.length < 1 ||
30
+ (items.length === 1 && !keepClick && defaultItem != null)) {
31
31
  return;
32
32
  }
33
33
  // Open the dialog
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 {
@@ -181,14 +181,13 @@ export function TableEx(props) {
181
181
  React.createElement(Table, { ...rest },
182
182
  React.createElement(TableHead, null,
183
183
  React.createElement(TableRow, { sx: {
184
- '& th': {
184
+ "& th": {
185
185
  backgroundColor: headerColors[0],
186
186
  color: headerColors[1]
187
187
  }
188
188
  } },
189
189
  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) }))),
190
+ React.createElement(Checkbox, { color: "primary", indeterminate: pageSelectedItems > 0 && pageSelectedItems < rows.length, checked: pageSelectedItems > 0, onChange: (_event, checked) => handleSelectAll(checked) }))),
192
191
  columns.map((column, index) => {
193
192
  // Destruct
194
193
  const { align, field, header, minWidth, sortable, sortAsc = true, type, width } = column;
@@ -198,7 +197,7 @@ export function TableEx(props) {
198
197
  let sortLabel;
199
198
  if (sortable && field != null) {
200
199
  const active = orderBy === field;
201
- sortLabel = (React.createElement(TableSortLabel, { active: active, direction: sortAsc ? 'asc' : 'desc', onClick: (_event) => {
200
+ sortLabel = (React.createElement(TableSortLabel, { active: active, direction: sortAsc ? "asc" : "desc", onClick: (_event) => {
202
201
  if (active)
203
202
  column.sortAsc = !sortAsc;
204
203
  handleSort(field, column.sortAsc);
@@ -216,18 +215,16 @@ export function TableEx(props) {
216
215
  } }, sortLabel));
217
216
  }))),
218
217
  React.createElement(TableBody, { sx: {
219
- '& tr:nth-of-type(odd):not(.Mui-selected)': {
218
+ "& tr:nth-of-type(odd):not(.Mui-selected)": {
220
219
  backgroundColor: alternatingColors[0]
221
220
  },
222
- '& tr:nth-of-type(even):not(.Mui-selected)': {
221
+ "& tr:nth-of-type(even):not(.Mui-selected)": {
223
222
  backgroundColor: alternatingColors[1]
224
223
  }
225
224
  } }, [...Array(batchSize)].map((_item, rowIndex) => {
226
225
  var _a;
227
226
  // Row
228
- const row = rowIndex < rows.length
229
- ? rows[rowIndex]
230
- : undefined;
227
+ const row = rowIndex < rows.length ? rows[rowIndex] : undefined;
231
228
  // Row id field value
232
229
  const rowId = (_a = DataTypes.getValue(row, idField)) !== null && _a !== void 0 ? _a : rowIndex;
233
230
  // Selected or not
@@ -245,7 +242,7 @@ export function TableEx(props) {
245
242
  };
246
243
  const cellProps = {
247
244
  align: GridAlignGet(align, type),
248
- valign: 'middle'
245
+ valign: "middle"
249
246
  };
250
247
  const child = row ? (cellRenderer({
251
248
  data: row,
@@ -257,7 +254,8 @@ export function TableEx(props) {
257
254
  type,
258
255
  rowIndex,
259
256
  columnIndex,
260
- cellProps
257
+ cellProps,
258
+ setItems: setRows
261
259
  })) : (React.createElement(React.Fragment, null, "\u00A0"));
262
260
  return (React.createElement(TableCell, { key: `${rowId}${columnIndex}`, ...cellProps }, child));
263
261
  })));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.1.90",
3
+ "version": "1.1.92",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -50,12 +50,12 @@
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",
57
- "@mui/icons-material": "^5.11.11",
58
- "@mui/material": "^5.11.15",
55
+ "@etsoo/react": "^1.6.59",
56
+ "@etsoo/shared": "^1.1.99",
57
+ "@mui/icons-material": "^5.11.16",
58
+ "@mui/material": "^5.11.16",
59
59
  "@mui/x-data-grid": "^6.0.4",
60
60
  "@types/pica": "^9.0.1",
61
61
  "@types/pulltorefreshjs": "^0.1.5",
@@ -70,7 +70,7 @@
70
70
  "react-avatar-editor": "^13.0.0",
71
71
  "react-dom": "^18.2.0",
72
72
  "react-draggable": "^4.4.5",
73
- "react-imask": "^6.4.3",
73
+ "react-imask": "^6.5.0",
74
74
  "react-router-dom": "^6.10.0",
75
75
  "react-window": "^1.8.8"
76
76
  },
@@ -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/ItemList.tsx CHANGED
@@ -6,13 +6,13 @@ import {
6
6
  ListItemText,
7
7
  DialogContent,
8
8
  Button,
9
- ListItemButton,
9
+ ListItemButton
10
10
  } from "@mui/material";
11
11
  import {
12
12
  DataTypes,
13
13
  IdDefaultType,
14
14
  LabelDefaultType,
15
- ListType,
15
+ ListType
16
16
  } from "@etsoo/shared";
17
17
 
18
18
  /**
@@ -118,7 +118,7 @@ export function ItemList<
118
118
  selectedValue,
119
119
  size = "medium",
120
120
  title,
121
- variant = "outlined",
121
+ variant = "outlined"
122
122
  } = props;
123
123
 
124
124
  // Get label
@@ -143,8 +143,10 @@ export function ItemList<
143
143
 
144
144
  // Click handler
145
145
  const clickHandler = () => {
146
- // More than one language
147
- if (items.length < 2) {
146
+ if (
147
+ items.length < 1 ||
148
+ (items.length === 1 && !keepClick && defaultItem != null)
149
+ ) {
148
150
  return;
149
151
  }
150
152
 
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,426 @@ 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
+ // Destruct states
292
+ const {
293
+ autoLoad: stateAutoLoad,
294
+ currentPage,
295
+ hasNextPage,
296
+ lastLoadedItems,
297
+ orderBy,
298
+ batchSize,
299
+ selectedItems
300
+ } = state;
301
+
302
+ // Current page selected items
303
+ const pageSelectedItems = selectable
304
+ ? rows.reduce((previousValue, currentItem) => {
305
+ if (
306
+ selectedItems.some((item) => item[idField] === currentItem[idField])
307
+ )
308
+ return previousValue + 1;
309
+
310
+ return previousValue;
311
+ }, 0)
312
+ : 0;
313
+
314
+ // Total rows
315
+ const totalRows = hasNextPage
316
+ ? -1
317
+ : currentPage * batchSize + (lastLoadedItems ?? 0);
318
+
319
+ // Auto load data when current page is 0
320
+ if (currentPage === 0 && stateAutoLoad && lastLoadedItems == null)
321
+ loadDataLocal();
322
+
323
+ React.useEffect(() => {
324
+ return () => {
325
+ state.isMounted = false;
326
+ };
327
+ }, []);
328
+
329
+ // Layout
330
+ return (
331
+ <Paper>
332
+ <TableContainer sx={{ maxHeight }}>
333
+ <Table {...rest}>
334
+ <TableHead>
335
+ <TableRow
336
+ sx={{
337
+ "& th": {
338
+ backgroundColor: headerColors[0],
339
+ color: headerColors[1]
340
+ }
341
+ }}
342
+ >
343
+ {selectable && (
344
+ <TableCell padding="checkbox">
345
+ <Checkbox
346
+ color="primary"
347
+ indeterminate={
348
+ pageSelectedItems > 0 && pageSelectedItems < rows.length
349
+ }
350
+ checked={pageSelectedItems > 0}
351
+ onChange={(_event, checked) => handleSelectAll(checked)}
352
+ />
353
+ </TableCell>
354
+ )}
355
+ {columns.map((column, index) => {
356
+ // Destruct
357
+ const {
358
+ align,
359
+ field,
360
+ header,
361
+ minWidth,
362
+ sortable,
363
+ sortAsc = true,
364
+ type,
365
+ width
366
+ } = column;
367
+
368
+ // Header text
369
+ const headerText = header ?? field;
370
+
371
+ // Sortable
372
+ let sortLabel: React.ReactNode;
373
+ if (sortable && field != null) {
374
+ const active = orderBy === field;
375
+
376
+ sortLabel = (
377
+ <TableSortLabel
378
+ active={active}
379
+ direction={sortAsc ? "asc" : "desc"}
380
+ onClick={(_event) => {
381
+ if (active) column.sortAsc = !sortAsc;
382
+
383
+ handleSort(field, column.sortAsc);
384
+ }}
434
385
  >
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
- );
386
+ {headerText}
387
+ </TableSortLabel>
388
+ );
389
+ } else {
390
+ sortLabel = headerText;
391
+ }
392
+
393
+ return (
394
+ <TableCell
395
+ align={GridAlignGet(align, type)}
396
+ key={field ?? index.toString()}
397
+ width={width}
398
+ sx={{
399
+ minWidth:
400
+ minWidth == null
401
+ ? width == null
402
+ ? TableExMinWidth
403
+ : undefined
404
+ : minWidth
405
+ }}
406
+ >
407
+ {sortLabel}
408
+ </TableCell>
409
+ );
410
+ })}
411
+ </TableRow>
412
+ </TableHead>
413
+ <TableBody
414
+ sx={{
415
+ "& tr:nth-of-type(odd):not(.Mui-selected)": {
416
+ backgroundColor: alternatingColors[0]
417
+ },
418
+ "& tr:nth-of-type(even):not(.Mui-selected)": {
419
+ backgroundColor: alternatingColors[1]
420
+ }
421
+ }}
422
+ >
423
+ {[...Array(batchSize)].map((_item, rowIndex) => {
424
+ // Row
425
+ const row = rowIndex < rows.length ? rows[rowIndex] : undefined;
426
+
427
+ // Row id field value
428
+ const rowId = DataTypes.getValue(row, idField) ?? rowIndex;
429
+
430
+ // Selected or not
431
+ const isItemSelected = selectable
432
+ ? selectedItems.some((item) => item[idField] === rowId)
433
+ : false;
434
+
435
+ return (
436
+ <TableRow
437
+ key={rowId as unknown as React.Key}
438
+ selected={isItemSelected}
439
+ >
440
+ {selectable && (
441
+ <TableCell padding="checkbox">
442
+ {row && (
443
+ <Checkbox
444
+ color="primary"
445
+ checked={isItemSelected}
446
+ onChange={(_event, checked) =>
447
+ handleSelect(row, checked)
448
+ }
449
+ />
450
+ )}
451
+ </TableCell>
452
+ )}
453
+ {columns.map(
454
+ (
455
+ {
456
+ align,
457
+ cellRenderer = DataGridRenderers.defaultCellRenderer,
458
+ field,
459
+ type,
460
+ valueFormatter
461
+ },
462
+ columnIndex
463
+ ) => {
464
+ const formatProps: GridCellFormatterProps<T> = {
465
+ data: row,
466
+ field,
467
+ rowIndex,
468
+ columnIndex
469
+ };
470
+
471
+ const cellProps: TableCellProps = {
472
+ align: GridAlignGet(align, type),
473
+ valign: "middle"
474
+ };
475
+
476
+ const child = row ? (
477
+ cellRenderer({
478
+ data: row,
479
+ field,
480
+ formattedValue: valueFormatter
481
+ ? valueFormatter(formatProps)
482
+ : undefined,
483
+ selected: isItemSelected,
484
+ type,
485
+ rowIndex,
486
+ columnIndex,
487
+ cellProps,
488
+ setItems: setRows
489
+ })
490
+ ) : (
491
+ <React.Fragment>&nbsp;</React.Fragment>
492
+ );
493
+
494
+ return (
495
+ <TableCell
496
+ key={`${rowId}${columnIndex}`}
497
+ {...cellProps}
498
+ >
499
+ {child}
500
+ </TableCell>
501
+ );
502
+ }
503
+ )}
504
+ </TableRow>
505
+ );
506
+ })}
507
+ </TableBody>
508
+ </Table>
509
+ </TableContainer>
510
+ <TablePagination
511
+ component="div"
512
+ showFirstButton
513
+ count={totalRows}
514
+ rowsPerPage={batchSize}
515
+ page={currentPage}
516
+ onPageChange={handleChangePage}
517
+ onRowsPerPageChange={handleChangeRowsPerPage}
518
+ rowsPerPageOptions={[
519
+ batchSize,
520
+ 2 * batchSize,
521
+ 5 * batchSize,
522
+ 10 * batchSize
523
+ ]}
524
+ />
525
+ </Paper>
526
+ );
559
527
  }