@etsoo/materialui 1.1.67 → 1.1.69

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.
@@ -1,132 +1,132 @@
1
- import { css } from '@emotion/css';
1
+ import { css } from "@emotion/css";
2
2
  import {
3
- GridAlignGet,
4
- GridCellFormatterProps,
5
- GridCellRendererProps,
6
- GridColumn,
7
- GridHeaderCellRendererProps,
8
- GridLoaderStates,
9
- ScrollerGrid,
10
- ScrollerGridForwardRef,
11
- ScrollerGridItemRendererProps,
12
- ScrollerGridProps,
13
- useCombinedRefs
14
- } from '@etsoo/react';
15
- import { DataTypes, IdDefaultType, Utils } from '@etsoo/shared';
3
+ GridAlignGet,
4
+ GridCellFormatterProps,
5
+ GridCellRendererProps,
6
+ GridColumn,
7
+ GridHeaderCellRendererProps,
8
+ GridLoaderStates,
9
+ ScrollerGrid,
10
+ ScrollerGridForwardRef,
11
+ ScrollerGridItemRendererProps,
12
+ ScrollerGridProps,
13
+ useCombinedRefs
14
+ } from "@etsoo/react";
15
+ import { DataTypes, IdDefaultType, Utils } from "@etsoo/shared";
16
16
  import {
17
- Box,
18
- BoxProps,
19
- Checkbox,
20
- Paper,
21
- TableSortLabel,
22
- useTheme
23
- } from '@mui/material';
24
- import React from 'react';
25
- import { DataGridRenderers } from './DataGridRenderers';
26
- import { MouseEventWithDataHandler } from './MUGlobal';
17
+ Box,
18
+ BoxProps,
19
+ Checkbox,
20
+ Paper,
21
+ TableSortLabel,
22
+ useTheme
23
+ } from "@mui/material";
24
+ import React from "react";
25
+ import { DataGridRenderers } from "./DataGridRenderers";
26
+ import { MouseEventWithDataHandler } from "./MUGlobal";
27
27
 
28
28
  /**
29
29
  * Footer item renderer props
30
30
  */
31
31
  export type DataGridExFooterItemRendererProps<T extends object> = {
32
- column: GridColumn<T>;
33
- index: number;
34
- states: GridLoaderStates<T>;
35
- cellProps: any;
36
- checkable: boolean;
32
+ column: GridColumn<T>;
33
+ index: number;
34
+ states: GridLoaderStates<T>;
35
+ cellProps: any;
36
+ checkable: boolean;
37
37
  };
38
38
 
39
39
  /**
40
40
  * Extended DataGrid with VariableSizeGrid props
41
41
  */
42
42
  export type DataGridExProps<
43
- T extends object,
44
- D extends DataTypes.Keys<T>
43
+ T extends object,
44
+ D extends DataTypes.Keys<T>
45
45
  > = Omit<
46
- ScrollerGridProps<T, D>,
47
- 'itemRenderer' | 'columnCount' | 'columnWidth' | 'width'
46
+ ScrollerGridProps<T, D>,
47
+ "itemRenderer" | "columnCount" | "columnWidth" | "width"
48
48
  > & {
49
- /**
50
- * Alternating colors for odd/even rows
51
- */
52
- alternatingColors?: [string?, string?];
53
-
54
- /**
55
- * Checkable to choose multiple items
56
- * @default false
57
- */
58
- checkable?: boolean;
59
-
60
- /**
61
- * Rows count to have the bottom border
62
- */
63
- borderRowsCount?: number;
64
-
65
- /**
66
- * Bottom height
67
- */
68
- bottomHeight?: number;
69
-
70
- /**
71
- * Columns
72
- */
73
- columns: GridColumn<T>[];
74
-
75
- /**
76
- * Footer item renderer
77
- */
78
- footerItemRenderer?: (
79
- rows: T[],
80
- props: DataGridExFooterItemRendererProps<T>
81
- ) => React.ReactNode;
82
-
83
- /**
84
- * Header height
85
- * @default 56
86
- */
87
- headerHeight?: number;
88
-
89
- /**
90
- * Hide the footer
91
- * @default false
92
- */
93
- hideFooter?: boolean;
94
-
95
- /**
96
- * Hover color
97
- */
98
- hoverColor?: string;
99
-
100
- /**
101
- * Double click handler
102
- */
103
- onDoubleClick?: MouseEventWithDataHandler<T>;
104
-
105
- /**
106
- * Click handler
107
- */
108
- onClick?: MouseEventWithDataHandler<T>;
109
-
110
- /**
111
- * Selectable to support hover over and out effect and row clickable
112
- * @default true
113
- */
114
- selectable?: boolean;
115
-
116
- /**
117
- * Selected color
118
- */
119
- selectedColor?: string;
120
-
121
- /**
122
- * Width
123
- */
124
- width?: number;
49
+ /**
50
+ * Alternating colors for odd/even rows
51
+ */
52
+ alternatingColors?: [string?, string?];
53
+
54
+ /**
55
+ * Checkable to choose multiple items
56
+ * @default false
57
+ */
58
+ checkable?: boolean;
59
+
60
+ /**
61
+ * Rows count to have the bottom border
62
+ */
63
+ borderRowsCount?: number;
64
+
65
+ /**
66
+ * Bottom height
67
+ */
68
+ bottomHeight?: number;
69
+
70
+ /**
71
+ * Columns
72
+ */
73
+ columns: GridColumn<T>[];
74
+
75
+ /**
76
+ * Footer item renderer
77
+ */
78
+ footerItemRenderer?: (
79
+ rows: T[],
80
+ props: DataGridExFooterItemRendererProps<T>
81
+ ) => React.ReactNode;
82
+
83
+ /**
84
+ * Header height
85
+ * @default 56
86
+ */
87
+ headerHeight?: number;
88
+
89
+ /**
90
+ * Hide the footer
91
+ * @default false
92
+ */
93
+ hideFooter?: boolean;
94
+
95
+ /**
96
+ * Hover color
97
+ */
98
+ hoverColor?: string;
99
+
100
+ /**
101
+ * Double click handler
102
+ */
103
+ onDoubleClick?: MouseEventWithDataHandler<T>;
104
+
105
+ /**
106
+ * Click handler
107
+ */
108
+ onClick?: MouseEventWithDataHandler<T>;
109
+
110
+ /**
111
+ * Selectable to support hover over and out effect and row clickable
112
+ * @default true
113
+ */
114
+ selectable?: boolean;
115
+
116
+ /**
117
+ * Selected color
118
+ */
119
+ selectedColor?: string;
120
+
121
+ /**
122
+ * Width
123
+ */
124
+ width?: number;
125
125
  };
126
126
 
127
127
  // Borders
128
- const boldBorder = '2px solid rgba(224, 224, 224, 1)';
129
- const thinBorder = '1px solid rgba(224, 224, 224, 1)';
128
+ const boldBorder = "2px solid rgba(224, 224, 224, 1)";
129
+ const thinBorder = "1px solid rgba(224, 224, 224, 1)";
130
130
 
131
131
  // Scroll bar size
132
132
  const scrollbarSize = 16;
@@ -135,52 +135,50 @@ const scrollbarSize = 16;
135
135
  const minWidth = 120;
136
136
 
137
137
  const createGridStyle = (
138
- alternatingColors: [string?, string?],
139
- selectedColor: string,
140
- hoverColor: string
138
+ alternatingColors: [string?, string?],
139
+ selectedColor: string,
140
+ hoverColor: string
141
141
  ) => {
142
- return css({
143
- '.DataGridEx-Selected': {
144
- backgroundColor: selectedColor
145
- },
146
- '.DataGridEx-Hover:not(.DataGridEx-Selected)': {
147
- backgroundColor: hoverColor
148
- },
149
- '& .DataGridEx-Cell0:not(.DataGridEx-Hover):not(.DataGridEx-Selected)':
150
- {
151
- backgroundColor: alternatingColors[0]
152
- },
153
- '& .DataGridEx-Cell1:not(.DataGridEx-Hover):not(.DataGridEx-Selected)':
154
- {
155
- backgroundColor: alternatingColors[1]
156
- },
157
- '& .DataGridEx-Cell-Border': {
158
- borderBottom: thinBorder
159
- }
160
- });
142
+ return css({
143
+ ".DataGridEx-Selected": {
144
+ backgroundColor: selectedColor
145
+ },
146
+ ".DataGridEx-Hover:not(.DataGridEx-Selected)": {
147
+ backgroundColor: hoverColor
148
+ },
149
+ "& .DataGridEx-Cell0:not(.DataGridEx-Hover):not(.DataGridEx-Selected)": {
150
+ backgroundColor: alternatingColors[0]
151
+ },
152
+ "& .DataGridEx-Cell1:not(.DataGridEx-Hover):not(.DataGridEx-Selected)": {
153
+ backgroundColor: alternatingColors[1]
154
+ },
155
+ "& .DataGridEx-Cell-Border": {
156
+ borderBottom: thinBorder
157
+ }
158
+ });
161
159
  };
162
160
 
163
161
  const rowItems = (
164
- div: HTMLDivElement,
165
- callback: (div: HTMLDivElement) => void
162
+ div: HTMLDivElement,
163
+ callback: (div: HTMLDivElement) => void
166
164
  ) => {
167
- const row = div.dataset['row'];
168
- if (div.parentElement == null || row == null) return;
169
- doRowItems(div.parentElement, parseFloat(row), callback);
165
+ const row = div.dataset["row"];
166
+ if (div.parentElement == null || row == null) return;
167
+ doRowItems(div.parentElement, parseFloat(row), callback);
170
168
  };
171
169
 
172
170
  const doRowItems = (
173
- parent: HTMLElement,
174
- rowIndex: number,
175
- callback: (div: HTMLDivElement) => void
171
+ parent: HTMLElement,
172
+ rowIndex: number,
173
+ callback: (div: HTMLDivElement) => void
176
174
  ) => {
177
- if (parent == null || rowIndex == null) return;
175
+ if (parent == null || rowIndex == null) return;
178
176
 
179
- parent
180
- ?.querySelectorAll<HTMLDivElement>(`div[data-row="${rowIndex}"]`)
181
- .forEach((rowItem) => {
182
- callback(rowItem);
183
- });
177
+ parent
178
+ ?.querySelectorAll<HTMLDivElement>(`div[data-row="${rowIndex}"]`)
179
+ .forEach((rowItem) => {
180
+ callback(rowItem);
181
+ });
184
182
  };
185
183
 
186
184
  /**
@@ -189,18 +187,18 @@ const doRowItems = (
189
187
  * @returns Total width and unset items
190
188
  */
191
189
  export function DataGridExCalColumns<T>(columns: GridColumn<T>[]) {
192
- return columns.reduce<{ total: number; unset: number }>(
193
- (previousValue, currentItem) => {
194
- previousValue.total +=
195
- currentItem.width ?? currentItem.minWidth ?? minWidth;
196
- if (currentItem.width == null) previousValue.unset++;
197
- return previousValue;
198
- },
199
- {
200
- total: 0,
201
- unset: 0
202
- }
203
- );
190
+ return columns.reduce<{ total: number; unset: number }>(
191
+ (previousValue, currentItem) => {
192
+ previousValue.total +=
193
+ currentItem.width ?? currentItem.minWidth ?? minWidth;
194
+ if (currentItem.width == null) previousValue.unset++;
195
+ return previousValue;
196
+ },
197
+ {
198
+ total: 0,
199
+ unset: 0
200
+ }
201
+ );
204
202
  }
205
203
 
206
204
  /**
@@ -209,505 +207,492 @@ export function DataGridExCalColumns<T>(columns: GridColumn<T>[]) {
209
207
  * @returns Component
210
208
  */
211
209
  export function DataGridEx<
212
- T extends object,
213
- D extends DataTypes.Keys<T> = IdDefaultType<T>
210
+ T extends object,
211
+ D extends DataTypes.Keys<T> = IdDefaultType<T>
214
212
  >(props: DataGridExProps<T, D>) {
215
- // Theme
216
- const theme = useTheme();
213
+ // Theme
214
+ const theme = useTheme();
217
215
 
218
- const defaultHeaderRenderer = (states: GridLoaderStates<T>) => {
219
- const { orderBy } = states;
220
- return (
216
+ const defaultHeaderRenderer = (states: GridLoaderStates<T>) => {
217
+ const { orderBy } = states;
218
+ return (
219
+ <Box
220
+ className="DataGridEx-Header"
221
+ display="flex"
222
+ alignItems="center"
223
+ borderBottom={boldBorder}
224
+ fontWeight={500}
225
+ minWidth={widthCalculator.total}
226
+ height={headerHeight}
227
+ >
228
+ {columns.map((column, index) => {
229
+ // Destruct
230
+ const {
231
+ align,
232
+ field,
233
+ header,
234
+ headerCellRenderer,
235
+ sortable,
236
+ sortAsc = true,
237
+ type
238
+ } = column;
239
+
240
+ // Header text
241
+ const headerText = header ?? field;
242
+
243
+ // Cell props
244
+ const cellProps: BoxProps = {};
245
+
246
+ // Sortable
247
+ let sortLabel: React.ReactNode;
248
+ if (headerCellRenderer) {
249
+ sortLabel = headerCellRenderer({
250
+ cellProps,
251
+ column,
252
+ columnIndex: checkable ? index - 1 : index, // Ignore the checkbox case,
253
+ states
254
+ });
255
+ } else if (sortable && field != null) {
256
+ const active = orderBy === field;
257
+
258
+ sortLabel = (
259
+ <TableSortLabel
260
+ active={active}
261
+ direction={sortAsc ? "asc" : "desc"}
262
+ onClick={(_event) => {
263
+ if (active) column.sortAsc = !sortAsc;
264
+
265
+ handleSort(field, column.sortAsc);
266
+ }}
267
+ >
268
+ {headerText}
269
+ </TableSortLabel>
270
+ );
271
+ } else {
272
+ sortLabel = headerText;
273
+ }
274
+
275
+ return (
221
276
  <Box
222
- className="DataGridEx-Header"
223
- display="flex"
224
- alignItems="center"
225
- borderBottom={boldBorder}
226
- fontWeight={500}
227
- minWidth={widthCalculator.total}
228
- height={headerHeight}
277
+ key={field ?? index.toString()}
278
+ textAlign={GridAlignGet(align, type)}
279
+ width={columnWidth(index)}
229
280
  >
230
- {columns.map((column, index) => {
231
- // Destruct
232
- const {
233
- align,
234
- field,
235
- header,
236
- headerCellRenderer,
237
- sortable,
238
- sortAsc = true,
239
- type
240
- } = column;
241
-
242
- // Header text
243
- const headerText = header ?? field;
244
-
245
- // Cell props
246
- const cellProps: BoxProps = {};
247
-
248
- // Sortable
249
- let sortLabel: React.ReactNode;
250
- if (headerCellRenderer) {
251
- sortLabel = headerCellRenderer({
252
- cellProps,
253
- column,
254
- columnIndex: checkable ? index - 1 : index, // Ignore the checkbox case,
255
- states
256
- });
257
- } else if (sortable && field != null) {
258
- const active = orderBy === field;
259
-
260
- sortLabel = (
261
- <TableSortLabel
262
- active={active}
263
- direction={sortAsc ? 'asc' : 'desc'}
264
- onClick={(_event) => {
265
- if (active) column.sortAsc = !sortAsc;
266
-
267
- handleSort(field, column.sortAsc);
268
- }}
269
- >
270
- {headerText}
271
- </TableSortLabel>
272
- );
273
- } else {
274
- sortLabel = headerText;
275
- }
276
-
277
- return (
278
- <Box
279
- key={field ?? index.toString()}
280
- textAlign={GridAlignGet(align, type)}
281
- width={columnWidth(index)}
282
- >
283
- <Box
284
- className="DataGridEx-Cell"
285
- onMouseEnter={handleMouseEnter}
286
- {...cellProps}
287
- >
288
- {sortLabel}
289
- </Box>
290
- </Box>
291
- );
292
- })}
281
+ <Box
282
+ className="DataGridEx-Cell"
283
+ onMouseEnter={handleMouseEnter}
284
+ {...cellProps}
285
+ >
286
+ {sortLabel}
287
+ </Box>
293
288
  </Box>
294
- );
295
- };
289
+ );
290
+ })}
291
+ </Box>
292
+ );
293
+ };
296
294
 
297
- function defaultFooterRenderer(rows: T[], states: GridLoaderStates<T>) {
298
- return (
295
+ function defaultFooterRenderer(rows: T[], states: GridLoaderStates<T>) {
296
+ return (
297
+ <Box
298
+ className="DataGridEx-Footer"
299
+ display="flex"
300
+ alignItems="center"
301
+ borderTop={thinBorder}
302
+ marginTop="1px"
303
+ minWidth={widthCalculator.total}
304
+ height={bottomHeight - 1}
305
+ >
306
+ {columns.map((column, index) => {
307
+ // Destruct
308
+ const { align, field, type } = column;
309
+
310
+ // Cell props
311
+ const cellProps: BoxProps = {};
312
+
313
+ // Cell
314
+ const cell = footerItemRenderer
315
+ ? footerItemRenderer(rows, {
316
+ column,
317
+ index: checkable ? index - 1 : index, // Ignore the checkbox case
318
+ states,
319
+ cellProps,
320
+ checkable
321
+ })
322
+ : undefined;
323
+
324
+ return (
299
325
  <Box
300
- className="DataGridEx-Footer"
301
- display="flex"
302
- alignItems="center"
303
- borderTop={thinBorder}
304
- marginTop="1px"
305
- minWidth={widthCalculator.total}
306
- height={bottomHeight - 1}
326
+ key={"bottom-" + (field ?? index.toString())}
327
+ textAlign={GridAlignGet(align, type)}
328
+ width={columnWidth(index)}
307
329
  >
308
- {columns.map((column, index) => {
309
- // Destruct
310
- const { align, field, type } = column;
311
-
312
- // Cell props
313
- const cellProps: BoxProps = {};
314
-
315
- // Cell
316
- const cell = footerItemRenderer
317
- ? footerItemRenderer(rows, {
318
- column,
319
- index: checkable ? index - 1 : index, // Ignore the checkbox case
320
- states,
321
- cellProps,
322
- checkable
323
- })
324
- : undefined;
325
-
326
- return (
327
- <Box
328
- key={'bottom-' + (field ?? index.toString())}
329
- textAlign={GridAlignGet(align, type)}
330
- width={columnWidth(index)}
331
- >
332
- <Box
333
- className="DataGridEx-Cell"
334
- onMouseEnter={handleMouseEnter}
335
- {...cellProps}
336
- >
337
- {cell}
338
- </Box>
339
- </Box>
340
- );
341
- })}
330
+ <Box
331
+ className="DataGridEx-Cell"
332
+ onMouseEnter={handleMouseEnter}
333
+ {...cellProps}
334
+ >
335
+ {cell}
336
+ </Box>
342
337
  </Box>
338
+ );
339
+ })}
340
+ </Box>
341
+ );
342
+ }
343
+
344
+ // Destruct
345
+ const {
346
+ alternatingColors = [theme.palette.grey[100], undefined],
347
+ borderRowsCount,
348
+ bottomHeight = 53,
349
+ checkable = false,
350
+ className,
351
+ columns,
352
+ defaultOrderBy,
353
+ height,
354
+ headerHeight = 56,
355
+ headerRenderer = defaultHeaderRenderer,
356
+ footerRenderer = defaultFooterRenderer,
357
+ footerItemRenderer = DataGridRenderers.defaultFooterItemRenderer,
358
+ hideFooter = false,
359
+ hoverColor = "#f6f9fb",
360
+ idField = "id" as D,
361
+ mRef = React.createRef(),
362
+ onClick,
363
+ onDoubleClick,
364
+ selectable = true,
365
+ selectedColor = "#edf4fb",
366
+ width,
367
+ ...rest
368
+ } = props;
369
+
370
+ if (checkable) {
371
+ const cbColumn: GridColumn<T> = {
372
+ field: "selected" as any, // Avoid validation from data model
373
+ header: "",
374
+ sortable: false,
375
+ width: 50,
376
+ cellRenderer: ({
377
+ cellProps,
378
+ data,
379
+ selected
380
+ }: GridCellRendererProps<T, BoxProps>) => {
381
+ cellProps.sx = {
382
+ padding: "4px!important"
383
+ };
384
+
385
+ return (
386
+ <Checkbox
387
+ color="primary"
388
+ checked={selected}
389
+ onChange={(_event, checked) => {
390
+ refs.current.ref?.selectItem(data, checked);
391
+ }}
392
+ />
343
393
  );
344
- }
394
+ },
395
+ headerCellRenderer: ({
396
+ cellProps,
397
+ states
398
+ }: GridHeaderCellRendererProps<T, BoxProps>) => {
399
+ // 2 = border height
400
+ const hpad = (headerHeight - 42) / 2;
401
+ cellProps.sx = {
402
+ padding: `${hpad}px 4px ${hpad - 1}px 4px!important`
403
+ };
345
404
 
346
- // Destruct
347
- const {
348
- alternatingColors = [theme.palette.grey[100], undefined],
349
- borderRowsCount,
350
- bottomHeight = 53,
351
- checkable = false,
352
- className,
353
- columns,
354
- defaultOrderBy,
355
- height,
356
- headerHeight = 56,
357
- headerRenderer = defaultHeaderRenderer,
358
- footerRenderer = defaultFooterRenderer,
359
- footerItemRenderer = DataGridRenderers.defaultFooterItemRenderer,
360
- hideFooter = false,
361
- hoverColor = '#f6f9fb',
362
- idField = 'id' as D,
363
- mRef = React.createRef(),
364
- onClick,
365
- onDoubleClick,
366
- selectable = true,
367
- selectedColor = '#edf4fb',
368
- width,
369
- ...rest
370
- } = props;
371
-
372
- if (checkable) {
373
- const cbColumn: GridColumn<T> = {
374
- field: 'selected' as any, // Avoid validation from data model
375
- header: '',
376
- sortable: false,
377
- width: 50,
378
- cellRenderer: ({
379
- cellProps,
380
- data,
381
- selected
382
- }: GridCellRendererProps<T, BoxProps>) => {
383
- cellProps.sx = {
384
- padding: '4px!important'
385
- };
386
-
387
- return (
388
- <Checkbox
389
- color="primary"
390
- checked={selected}
391
- onChange={(_event, checked) => {
392
- refs.current.ref?.selectItem(data, checked);
393
- }}
394
- />
395
- );
396
- },
397
- headerCellRenderer: ({
398
- cellProps,
399
- states
400
- }: GridHeaderCellRendererProps<T, BoxProps>) => {
401
- // 2 = border height
402
- const hpad = (headerHeight - 42) / 2;
403
- cellProps.sx = {
404
- padding: `${hpad}px 4px ${hpad - 1}px 4px!important`
405
- };
406
-
407
- return (
408
- <Checkbox
409
- color="primary"
410
- indeterminate={
411
- states.selectedItems.length > 0 &&
412
- states.selectedItems.length < states.loadedItems
413
- }
414
- checked={states.selectedItems.length > 0}
415
- onChange={(_event, checked) =>
416
- refs.current.ref?.selectAll(checked)
417
- }
418
- />
419
- );
405
+ return (
406
+ <Checkbox
407
+ color="primary"
408
+ indeterminate={
409
+ states.selectedItems.length > 0 &&
410
+ states.selectedItems.length < states.loadedItems
420
411
  }
421
- };
412
+ checked={states.selectedItems.length > 0}
413
+ onChange={(_event, checked) => refs.current.ref?.selectAll(checked)}
414
+ />
415
+ );
416
+ }
417
+ };
422
418
 
423
- // Update to the latest version
424
- if (columns[0].field === 'selected') {
425
- columns[0] = cbColumn;
426
- } else {
427
- columns.unshift(cbColumn);
428
- }
419
+ // Update to the latest version
420
+ if (columns[0].field === "selected") {
421
+ columns[0] = cbColumn;
422
+ } else {
423
+ columns.unshift(cbColumn);
429
424
  }
425
+ }
426
+
427
+ const refs = React.useRef<{ ref?: ScrollerGridForwardRef<T> }>({});
428
+
429
+ const mRefLocal = useCombinedRefs(mRef, (ref: ScrollerGridForwardRef<T>) => {
430
+ if (ref == null) return;
431
+ refs.current.ref = ref;
432
+ });
433
+
434
+ // New sort
435
+ const handleSort = (field: string, asc?: boolean) => {
436
+ reset({ orderBy: field, orderByAsc: asc });
437
+ };
438
+
439
+ // Reset
440
+ const reset = (add: object) => {
441
+ refs.current.ref?.reset(add);
442
+ };
443
+
444
+ // Show hover tooltip for trucated text
445
+ const handleMouseEnter = (event: React.MouseEvent<HTMLDivElement>) => {
446
+ const div = event.currentTarget;
447
+ const { innerText, offsetWidth, scrollWidth } = div;
448
+ if (offsetWidth < scrollWidth) {
449
+ div.title = innerText;
450
+ } else {
451
+ div.title = "";
452
+ }
453
+ };
430
454
 
431
- const refs = React.useRef<{ ref?: ScrollerGridForwardRef<T> }>({});
432
-
433
- const mRefLocal = useCombinedRefs(
434
- mRef,
435
- (ref: ScrollerGridForwardRef<T>) => {
436
- if (ref == null) return;
437
- refs.current.ref = ref;
438
- }
439
- );
440
-
441
- // New sort
442
- const handleSort = (field: string, asc?: boolean) => {
443
- reset({ orderBy: field, orderByAsc: asc });
444
- };
445
-
446
- // Reset
447
- const reset = (add: object) => {
448
- refs.current.ref?.reset(add);
449
- };
455
+ // selectedRowIndex state
456
+ const selectedRowIndex = React.useRef(-1);
450
457
 
451
- // Show hover tooltip for trucated text
452
- const handleMouseEnter = (event: React.MouseEvent<HTMLDivElement>) => {
453
- const div = event.currentTarget;
454
- const { innerText, offsetWidth, scrollWidth } = div;
455
- if (offsetWidth < scrollWidth) {
456
- div.title = innerText;
457
- } else {
458
- div.title = '';
459
- }
460
- };
458
+ const handleMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
459
+ const div = event.currentTarget;
460
+ const row = div.dataset["row"];
461
+ if (div.parentElement == null || row == null) return;
461
462
 
462
- // selectedRowIndex state
463
- const selectedRowIndex = React.useRef(-1);
463
+ const rowIndex = parseFloat(row);
464
464
 
465
- const handleMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
466
- const div = event.currentTarget;
467
- const row = div.dataset['row'];
468
- if (div.parentElement == null || row == null) return;
465
+ // No change
466
+ if (isNaN(rowIndex) || rowIndex === selectedRowIndex.current) return;
469
467
 
470
- const rowIndex = parseFloat(row);
468
+ if (selectedRowIndex.current != -1) {
469
+ doRowItems(div.parentElement, selectedRowIndex.current, (preDiv) => {
470
+ preDiv.classList.remove("DataGridEx-Selected");
471
+ });
472
+ }
471
473
 
472
- // No change
473
- if (isNaN(rowIndex) || rowIndex === selectedRowIndex.current) return;
474
+ rowItems(div, (currentDiv) => {
475
+ currentDiv.classList.add("DataGridEx-Selected");
476
+ });
474
477
 
475
- if (selectedRowIndex.current != -1) {
476
- doRowItems(
477
- div.parentElement,
478
- selectedRowIndex.current,
479
- (preDiv) => {
480
- preDiv.classList.remove('DataGridEx-Selected');
481
- }
482
- );
483
- }
478
+ selectedRowIndex.current = rowIndex;
479
+ };
484
480
 
485
- rowItems(div, (currentDiv) => {
486
- currentDiv.classList.add('DataGridEx-Selected');
487
- });
481
+ const handleMouseOver = (event: React.MouseEvent<HTMLDivElement>) => {
482
+ rowItems(event.currentTarget, (div) => {
483
+ div.classList.add("DataGridEx-Hover");
484
+ });
485
+ };
488
486
 
489
- selectedRowIndex.current = rowIndex;
487
+ const handleMouseOut = (event: React.MouseEvent<HTMLDivElement>) => {
488
+ rowItems(event.currentTarget, (div) => {
489
+ div.classList.remove("DataGridEx-Hover");
490
+ });
491
+ };
492
+
493
+ /**
494
+ * Item renderer
495
+ */
496
+ const itemRenderer = ({
497
+ columnIndex,
498
+ rowIndex,
499
+ style,
500
+ data,
501
+ selectedItems
502
+ }: ScrollerGridItemRendererProps<T>) => {
503
+ // Column
504
+ const {
505
+ align,
506
+ cellRenderer = DataGridRenderers.defaultCellRenderer,
507
+ cellBoxStyle,
508
+ field,
509
+ type,
510
+ valueFormatter,
511
+ renderProps
512
+ } = columns[columnIndex];
513
+
514
+ // Props
515
+ const formatProps: GridCellFormatterProps<T> = {
516
+ data,
517
+ field,
518
+ rowIndex,
519
+ columnIndex
490
520
  };
491
521
 
492
- const handleMouseOver = (event: React.MouseEvent<HTMLDivElement>) => {
493
- rowItems(event.currentTarget, (div) => {
494
- div.classList.add('DataGridEx-Hover');
495
- });
496
- };
522
+ let rowClass = `DataGridEx-Cell${rowIndex % 2}`;
523
+ if (
524
+ borderRowsCount != null &&
525
+ borderRowsCount > 0 &&
526
+ (rowIndex + 1) % borderRowsCount === 0
527
+ ) {
528
+ rowClass += ` DataGridEx-Cell-Border`;
529
+ }
497
530
 
498
- const handleMouseOut = (event: React.MouseEvent<HTMLDivElement>) => {
499
- rowItems(event.currentTarget, (div) => {
500
- div.classList.remove('DataGridEx-Hover');
501
- });
531
+ // Selected
532
+ const selected =
533
+ data != null &&
534
+ (selectedRowIndex.current === rowIndex ||
535
+ selectedItems.some(
536
+ (selectedItem) =>
537
+ selectedItem != null && selectedItem[idField] === data[idField]
538
+ ));
539
+
540
+ if (selected) {
541
+ rowClass += ` DataGridEx-Selected`;
542
+ }
543
+
544
+ // Box style
545
+ const boxStyle =
546
+ data == null || cellBoxStyle == null
547
+ ? undefined
548
+ : typeof cellBoxStyle === "function"
549
+ ? cellBoxStyle(data)
550
+ : cellBoxStyle;
551
+
552
+ const cellProps: BoxProps = {
553
+ className: "DataGridEx-Cell",
554
+ textAlign: GridAlignGet(align, type),
555
+ sx: { ...boxStyle }
502
556
  };
503
557
 
504
- /**
505
- * Item renderer
506
- */
507
- const itemRenderer = ({
508
- columnIndex,
509
- rowIndex,
510
- style,
511
- data,
512
- selectedItems
513
- }: ScrollerGridItemRendererProps<T>) => {
514
- // Column
515
- const {
516
- align,
517
- cellRenderer = DataGridRenderers.defaultCellRenderer,
518
- field,
519
- type,
520
- valueFormatter,
521
- renderProps
522
- } = columns[columnIndex];
523
-
524
- // Props
525
- const formatProps: GridCellFormatterProps<T> = {
526
- data,
527
- field,
528
- rowIndex,
529
- columnIndex
530
- };
558
+ const child = cellRenderer({
559
+ data,
560
+ field,
561
+ formattedValue: valueFormatter ? valueFormatter(formatProps) : undefined,
562
+ selected,
563
+ type,
564
+ rowIndex,
565
+ columnIndex,
566
+ cellProps,
567
+ renderProps
568
+ });
531
569
 
532
- let rowClass = `DataGridEx-Cell${rowIndex % 2}`;
533
- if (
534
- borderRowsCount != null &&
535
- borderRowsCount > 0 &&
536
- (rowIndex + 1) % borderRowsCount === 0
537
- ) {
538
- rowClass += ` DataGridEx-Cell-Border`;
570
+ return (
571
+ <div
572
+ className={rowClass}
573
+ style={style}
574
+ data-row={rowIndex}
575
+ data-column={columnIndex}
576
+ onMouseDown={selectable && !checkable ? handleMouseDown : undefined}
577
+ onMouseOver={selectable ? handleMouseOver : undefined}
578
+ onMouseOut={selectable ? handleMouseOut : undefined}
579
+ onClick={(event) => onClick && data != null && onClick(event, data)}
580
+ onDoubleClick={(event) =>
581
+ onDoubleClick && data != null && onDoubleClick(event, data)
539
582
  }
583
+ >
584
+ <Box {...cellProps} onMouseEnter={handleMouseEnter}>
585
+ {child}
586
+ </Box>
587
+ </div>
588
+ );
589
+ };
590
+
591
+ // Column width calculator
592
+ const widthCalculator = React.useMemo(
593
+ () => DataGridExCalColumns(columns),
594
+ [columns]
595
+ );
596
+
597
+ // Column width
598
+ const columnWidth = React.useCallback(
599
+ (index: number) => {
600
+ // Ignore null case
601
+ if (width == null) return 0;
602
+
603
+ // Column
604
+ const column = columns[index];
605
+ if (column.width != null) return column.width;
606
+
607
+ // More space
608
+ const leftWidth =
609
+ width - widthCalculator.total - (width < 800 ? 0 : scrollbarSize);
610
+
611
+ // Shared width
612
+ const sharedWidth = leftWidth > 0 ? leftWidth / widthCalculator.unset : 0;
613
+
614
+ return (column.minWidth || minWidth) + sharedWidth;
615
+ },
616
+ [columns, width]
617
+ );
618
+
619
+ // Table
620
+ const table = React.useMemo(() => {
621
+ const defaultOrderByAsc = defaultOrderBy
622
+ ? columns.find((column) => column.field === defaultOrderBy)?.sortAsc
623
+ : undefined;
540
624
 
541
- // Selected
542
- const selected =
543
- data != null &&
544
- (selectedRowIndex.current === rowIndex ||
545
- selectedItems.some(
546
- (selectedItem) =>
547
- selectedItem != null &&
548
- selectedItem[idField] === data[idField]
549
- ));
550
-
551
- if (selected) {
552
- rowClass += ` DataGridEx-Selected`;
625
+ return (
626
+ <ScrollerGrid<T, D>
627
+ className={Utils.mergeClasses(
628
+ "DataGridEx-Body",
629
+ "DataGridEx-CustomBar",
630
+ className,
631
+ createGridStyle(alternatingColors, selectedColor, hoverColor)
632
+ )}
633
+ columnCount={columns.length}
634
+ columnWidth={columnWidth}
635
+ defaultOrderBy={defaultOrderBy}
636
+ defaultOrderByAsc={defaultOrderByAsc}
637
+ height={
638
+ height -
639
+ headerHeight -
640
+ (hideFooter ? 0 : bottomHeight + 1) -
641
+ scrollbarSize
553
642
  }
554
-
555
- const cellProps: BoxProps = {
556
- className: 'DataGridEx-Cell',
557
- textAlign: GridAlignGet(align, type)
558
- };
559
-
560
- const child = cellRenderer({
561
- data,
562
- field,
563
- formattedValue: valueFormatter
564
- ? valueFormatter(formatProps)
565
- : undefined,
566
- selected,
567
- type,
568
- rowIndex,
569
- columnIndex,
570
- cellProps,
571
- renderProps
572
- });
573
-
574
- return (
575
- <div
576
- className={rowClass}
577
- style={style}
578
- data-row={rowIndex}
579
- data-column={columnIndex}
580
- onMouseDown={
581
- selectable && !checkable ? handleMouseDown : undefined
582
- }
583
- onMouseOver={selectable ? handleMouseOver : undefined}
584
- onMouseOut={selectable ? handleMouseOut : undefined}
585
- onClick={(event) =>
586
- onClick && data != null && onClick(event, data)
587
- }
588
- onDoubleClick={(event) =>
589
- onDoubleClick && data != null && onDoubleClick(event, data)
590
- }
591
- >
592
- <Box {...cellProps} onMouseEnter={handleMouseEnter}>
593
- {child}
594
- </Box>
595
- </div>
596
- );
597
- };
598
-
599
- // Column width calculator
600
- const widthCalculator = React.useMemo(
601
- () => DataGridExCalColumns(columns),
602
- [columns]
643
+ headerRenderer={headerRenderer}
644
+ idField={idField}
645
+ itemRenderer={itemRenderer}
646
+ footerRenderer={hideFooter ? undefined : footerRenderer}
647
+ width={Math.max(width ?? 0, widthCalculator.total)}
648
+ mRef={mRefLocal}
649
+ {...rest}
650
+ />
603
651
  );
652
+ }, [width]);
604
653
 
605
- // Column width
606
- const columnWidth = React.useCallback(
607
- (index: number) => {
608
- // Ignore null case
609
- if (width == null) return 0;
610
-
611
- // Column
612
- const column = columns[index];
613
- if (column.width != null) return column.width;
614
-
615
- // More space
616
- const leftWidth =
617
- width -
618
- widthCalculator.total -
619
- (width < 800 ? 0 : scrollbarSize);
620
-
621
- // Shared width
622
- const sharedWidth =
623
- leftWidth > 0 ? leftWidth / widthCalculator.unset : 0;
624
-
625
- return (column.minWidth || minWidth) + sharedWidth;
654
+ return (
655
+ <Paper
656
+ sx={{
657
+ fontSize: "0.875rem",
658
+ height,
659
+ "& .DataGridEx-Cell": {
660
+ padding: 2,
661
+ whiteSpace: "nowrap",
662
+ overflow: "hidden",
663
+ textOverflow: "ellipsis"
626
664
  },
627
- [columns, width]
628
- );
629
-
630
- // Table
631
- const table = React.useMemo(() => {
632
- const defaultOrderByAsc = defaultOrderBy
633
- ? columns.find((column) => column.field === defaultOrderBy)?.sortAsc
634
- : undefined;
635
-
636
- return (
637
- <ScrollerGrid<T, D>
638
- className={Utils.mergeClasses(
639
- 'DataGridEx-Body',
640
- 'DataGridEx-CustomBar',
641
- className,
642
- createGridStyle(
643
- alternatingColors,
644
- selectedColor,
645
- hoverColor
646
- )
647
- )}
648
- columnCount={columns.length}
649
- columnWidth={columnWidth}
650
- defaultOrderBy={defaultOrderBy}
651
- defaultOrderByAsc={defaultOrderByAsc}
652
- height={
653
- height -
654
- headerHeight -
655
- (hideFooter ? 0 : bottomHeight + 1) -
656
- scrollbarSize
657
- }
658
- headerRenderer={headerRenderer}
659
- idField={idField}
660
- itemRenderer={itemRenderer}
661
- footerRenderer={hideFooter ? undefined : footerRenderer}
662
- width={Math.max(width ?? 0, widthCalculator.total)}
663
- mRef={mRefLocal}
664
- {...rest}
665
- />
666
- );
667
- }, [width]);
668
-
669
- return (
670
- <Paper
671
- sx={{
672
- fontSize: '0.875rem',
673
- height,
674
- '& .DataGridEx-Cell': {
675
- padding: 2,
676
- whiteSpace: 'nowrap',
677
- overflow: 'hidden',
678
- textOverflow: 'ellipsis'
679
- },
680
- '& .DataGridEx-CustomBar': {
681
- '@media (min-width: 800px)': {
682
- '::-webkit-scrollbar': {
683
- width: scrollbarSize,
684
- height: scrollbarSize,
685
- backgroundColor: '#f6f6f6'
686
- },
687
- '::-webkit-scrollbar-thumb': {
688
- backgroundColor: 'rgba(0,0,0,0.4)',
689
- borderRadius: '2px'
690
- },
691
- '::-webkit-scrollbar-track-piece:start': {
692
- background: 'transparent'
693
- },
694
- '::-webkit-scrollbar-track-piece:end': {
695
- background: 'transparent'
696
- }
697
- }
698
- }
699
- }}
700
- >
701
- <div
702
- className="DataGridEx-CustomBar"
703
- style={{
704
- width,
705
- overflowX: 'auto',
706
- overflowY: 'hidden'
707
- }}
708
- >
709
- {table}
710
- </div>
711
- </Paper>
712
- );
665
+ "& .DataGridEx-CustomBar": {
666
+ "@media (min-width: 800px)": {
667
+ "::-webkit-scrollbar": {
668
+ width: scrollbarSize,
669
+ height: scrollbarSize,
670
+ backgroundColor: "#f6f6f6"
671
+ },
672
+ "::-webkit-scrollbar-thumb": {
673
+ backgroundColor: "rgba(0,0,0,0.4)",
674
+ borderRadius: "2px"
675
+ },
676
+ "::-webkit-scrollbar-track-piece:start": {
677
+ background: "transparent"
678
+ },
679
+ "::-webkit-scrollbar-track-piece:end": {
680
+ background: "transparent"
681
+ }
682
+ }
683
+ }
684
+ }}
685
+ >
686
+ <div
687
+ className="DataGridEx-CustomBar"
688
+ style={{
689
+ width,
690
+ overflowX: "auto",
691
+ overflowY: "hidden"
692
+ }}
693
+ >
694
+ {table}
695
+ </div>
696
+ </Paper>
697
+ );
713
698
  }