@monolith-forensics/monolith-ui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,729 @@
1
+ import {
2
+ ColumnDef,
3
+ flexRender,
4
+ getCoreRowModel,
5
+ getSortedRowModel,
6
+ Row,
7
+ SortingState,
8
+ useReactTable,
9
+ createColumnHelper,
10
+ getFacetedUniqueValues,
11
+ getFilteredRowModel,
12
+ } from "@tanstack/react-table";
13
+ import { useMemo, useState } from "react";
14
+ import { useVirtualizer } from "@tanstack/react-virtual";
15
+ import { useRef } from "react";
16
+ import { useReducer } from "react";
17
+ import styled from "@emotion/styled";
18
+ import NorthIcon from "@mui/icons-material/North";
19
+ import SouthIcon from "@mui/icons-material/South";
20
+ import FilterListIcon from "@mui/icons-material/FilterList";
21
+ import Menu, { MenuItem } from "../Menu";
22
+ import moment from "moment";
23
+ import {
24
+ getDateFormat,
25
+ monolithMoment,
26
+ } from "../../../../../utils/date-format";
27
+ import { useEffect } from "react";
28
+ import CheckBox from "../CheckBox";
29
+
30
+ const clearFiltersEvent = new Event("clearFilters");
31
+
32
+ const columnShape = {
33
+ dataField: "string",
34
+ caption: "string",
35
+ dataType: "string",
36
+ visible: "boolean",
37
+ cellRender: "function",
38
+ allowResizing: "boolean",
39
+ allowSorting: "boolean",
40
+ allowReordering: "boolean",
41
+ showInColumnChooser: "boolean",
42
+ };
43
+
44
+ const DataGrid = ({
45
+ dataGridRef,
46
+ data = [],
47
+ columns = [],
48
+ columnChooser,
49
+ ...props
50
+ }) => {
51
+ const tableContainerRef = useRef();
52
+ const [gridData, setGridData] = useState(() => [...data]);
53
+ const [sorting, setSorting] = useState([]);
54
+ const [columnResizeMode, setColumnResizeMode] = useState("onChange");
55
+ const [globalFilter, setGlobalFilter] = useState("");
56
+ const [columnOrder, setColumnOrder] = useState([]);
57
+ const [columnFilters, setColumnFilters] = useState([]);
58
+ const [columnVisibility, setColumnVisibility] = useState({});
59
+ const [rowSelection, setRowSelection] = useState({});
60
+
61
+ //Create columns from data with column helper
62
+ const gridColumns = useMemo(
63
+ () => createColumnDefs(data, columns, props),
64
+ [data, columns]
65
+ );
66
+
67
+ const table = useReactTable({
68
+ data: gridData,
69
+ columns: gridColumns,
70
+ columnResizeMode,
71
+ state: {
72
+ sorting,
73
+ columnFilters,
74
+ globalFilter,
75
+ columnOrder,
76
+ columnVisibility,
77
+ rowSelection,
78
+ },
79
+ onSortingChange: setSorting,
80
+ onRowSelectionChange: setRowSelection,
81
+ onColumnVisibilityChange: setColumnVisibility,
82
+ getCoreRowModel: getCoreRowModel(),
83
+ getSortedRowModel: getSortedRowModel(),
84
+ getFacetedUniqueValues: getFacetedUniqueValues(),
85
+ getFilteredRowModel: getFilteredRowModel(),
86
+ onColumnOrderChange: setColumnOrder,
87
+ onColumnFiltersChange: setColumnFilters,
88
+ onGlobalFilterChange: setGlobalFilter,
89
+ // debugTable: true,
90
+ // debugHeaders: true,
91
+ // debugColumns: true,
92
+ });
93
+
94
+ const { rows } = table.getRowModel();
95
+
96
+ const rowVirtualizer = useVirtualizer({
97
+ count: data.length,
98
+ getScrollElement: () => tableContainerRef.current,
99
+ estimateSize: (i) => 37,
100
+ overscan: 5,
101
+ });
102
+
103
+ useEffect(() => {
104
+ if (dataGridRef)
105
+ dataGridRef.current = {
106
+ clearFilters: () => {
107
+ document.dispatchEvent(clearFiltersEvent);
108
+ table.resetColumnFilters();
109
+ },
110
+ };
111
+ }, []);
112
+
113
+ return (
114
+ <TableContainer ref={tableContainerRef} width={props.width}>
115
+ <ColumnChooser parentEl={columnChooser} columns={columns} table={table} />
116
+ <Table>
117
+ <TableHead>
118
+ {table.getHeaderGroups().map((headerGroup) => (
119
+ <TableHeaderRow key={headerGroup.id}>
120
+ {headerGroup.headers.map((header) => (
121
+ <TableHeaderCell
122
+ key={header.id}
123
+ header={header}
124
+ columnFilters={columnFilters}
125
+ >
126
+ {header.isPlaceholder
127
+ ? null
128
+ : flexRender(
129
+ header.column.columnDef.header,
130
+ header.getContext()
131
+ )}
132
+ </TableHeaderCell>
133
+ ))}
134
+ </TableHeaderRow>
135
+ ))}
136
+ </TableHead>
137
+ <TableBody>
138
+ {/* {rowVirtualizer.getVirtualItems().map((virtualRow) => {
139
+ const row = rows[virtualRow.index];
140
+ return (
141
+ <TableRow key={row.id}>
142
+ {row.getVisibleCells().map((cell) => (
143
+ <TableCell key={cell.id} row={row} cell={cell}>
144
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
145
+ </TableCell>
146
+ ))}
147
+ </TableRow>
148
+ );
149
+ })} */}
150
+ {table.getRowModel().rows.map((row) => (
151
+ <TableRow key={row.id}>
152
+ {row.getVisibleCells().map((cell) => (
153
+ <TableCell key={cell.id} row={row} cell={cell}>
154
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
155
+ </TableCell>
156
+ ))}
157
+ </TableRow>
158
+ ))}
159
+ </TableBody>
160
+ </Table>
161
+ </TableContainer>
162
+ );
163
+ };
164
+
165
+ const ColumnChooser = styled(
166
+ ({ className, table, columns, parentEl, ...props }) => {
167
+ const [anchorEl, setAnchorEl] = useState(null);
168
+ const [selections, setSelections] = useState(
169
+ columns.map((c) => c.dataField)
170
+ );
171
+
172
+ const open = Boolean(anchorEl);
173
+
174
+ const leafColumns = useMemo(
175
+ () =>
176
+ table
177
+ .getAllLeafColumns()
178
+ .map((tcol) => {
179
+ tcol.caption =
180
+ columns.find((col) => col.dataField === tcol.id)?.caption || null;
181
+ return tcol;
182
+ })
183
+ .filter((tcol) => !!tcol.caption),
184
+ [table]
185
+ );
186
+
187
+ const handleClick = (event) => {
188
+ event.stopPropagation();
189
+ setAnchorEl(event.target);
190
+ };
191
+
192
+ const handleClose = () => {
193
+ setAnchorEl(null);
194
+ };
195
+
196
+ const handleSelect = (event) => {
197
+ setSelections((prev) => {
198
+ let newState;
199
+ if (prev.includes(event.id)) {
200
+ newState = prev.filter((item) => item !== event.id);
201
+ } else {
202
+ newState = [...prev, event.id];
203
+ }
204
+ event.toggleVisibility();
205
+ return newState;
206
+ });
207
+ };
208
+
209
+ useEffect(() => {
210
+ parentEl?.current?.addEventListener("click", handleClick);
211
+
212
+ return () => {
213
+ parentEl?.current?.removeEventListener("click", handleClick);
214
+ };
215
+ }, []);
216
+
217
+ return (
218
+ <div className={className}>
219
+ <Menu
220
+ anchorEl={anchorEl}
221
+ open={open}
222
+ onClose={handleClose}
223
+ onItemSelect={handleSelect}
224
+ dropDownStyle={{ padding: 10 }}
225
+ >
226
+ {leafColumns.map((item) => {
227
+ const isChecked = selections.includes(item.id);
228
+ return (
229
+ <MenuItem
230
+ key={item.id}
231
+ onClick={handleClose}
232
+ data={item}
233
+ style={{
234
+ margin: "5px 0px",
235
+ display: "flex",
236
+ alignItems: "center",
237
+ }}
238
+ >
239
+ <div>
240
+ <CheckBox
241
+ checked={isChecked}
242
+ onChange={() => handleSelect(item)}
243
+ style={{ marginRight: 5 }}
244
+ />
245
+ </div>
246
+ <div>{item.caption}</div>
247
+ </MenuItem>
248
+ );
249
+ })}
250
+ </Menu>
251
+ </div>
252
+ );
253
+ }
254
+ )((props) => ({}));
255
+
256
+ const allPossibleFormats = [
257
+ "D MMMM YYYY",
258
+ "D MMMM YYYY HH:mm",
259
+ "DD-MM-YY",
260
+ "DD-MM-YYYY",
261
+ "DD.MM.YYYY",
262
+ "DD.MM.YYYY HH:mm",
263
+ "DD/MM/YY",
264
+ "DD/MM/YYYY",
265
+ "DD/MM/YYYY HH:mm:ss",
266
+ "HH:mm:ss",
267
+ "M/D/YYYY",
268
+ "D/M/YYYY",
269
+ "D/M/YYYY HH:mm:ss",
270
+ "D/M/YYYY HH:mm",
271
+ "MM-DD-YY",
272
+ "MM-DD-YYYY",
273
+ "MM-DD-YYYY HH:mm:ss",
274
+ "MM/DD/YY",
275
+ "MM/DD/YYYY",
276
+ "MM/DD/YYYY HH:mm:ss",
277
+ "MMM D YYYY",
278
+ "MMM D YYYY LT",
279
+ "MMMM Do YYYY",
280
+ "MMMM Do YYYY LT",
281
+ "YYYY-DD-MM HH:mm:ss",
282
+ "YYYY-MM",
283
+ "YYYY-MM-DD",
284
+ "YYYY-MM-DD HH:mm",
285
+ "YYYY-MM-DD HH:mm:ss",
286
+ "YYYY-MM-DD LT",
287
+ "YYYY-MM-DD h:mm:ss A",
288
+ "YYYY-MM-DDTHH:mm:ssZ",
289
+ "ddd, MMM D YYYY LT",
290
+ "dddd D MMMM YYYY HH:mm",
291
+ "dddd, MMMM Do YYYY LT",
292
+ "YYYY-MM-DD HH:mm:ss.SSS",
293
+ "YYYY-MM-DD HH:mm:ss.SSSZ",
294
+ "YYYY-MM-DDTHH:mm:ss.SSSZ",
295
+ "YYYY-MM-DDTHH:mm:ss.SSZ",
296
+ "YYYY-MM-DDTHH:mm:ssZ",
297
+ ];
298
+
299
+ const isValidTimestamp = (timestamp) => {
300
+ const currentFormat = getDateFormat();
301
+ const date = moment(timestamp, allPossibleFormats, true).toDate();
302
+ return date instanceof Date && !isNaN(date);
303
+ };
304
+
305
+ const filterFn = (row, columnId, value, addMeta) => {
306
+ const column = row.columns[columnId];
307
+ const cell = row.cells[columnId];
308
+ const cellValue = cell.value;
309
+ const cellValueString = cellValue.toString();
310
+ const cellValueLowerCase = cellValueString.toLowerCase();
311
+ const valueLowerCase = value.toLowerCase();
312
+ const isMatch = cellValueLowerCase.includes(valueLowerCase);
313
+ if (isMatch) {
314
+ addMeta({ cell, column });
315
+ }
316
+ return isMatch;
317
+ };
318
+
319
+ const createColumnDefs = (data, columns, props) => {
320
+ const columnHelper = createColumnHelper();
321
+ let columnDefs = [];
322
+
323
+ if (props?.enableSelection) {
324
+ columnDefs.push({
325
+ id: "select",
326
+ caption: "select",
327
+ columnType: "select",
328
+ enableFilter: false,
329
+ enableSorting: false,
330
+ header: ({ table }) => (
331
+ <CheckBox
332
+ checked={table.getIsAllRowsSelected()}
333
+ indeterminate={table.getIsSomeRowsSelected()}
334
+ onChange={(e) => table.toggleAllRowsSelected()}
335
+ />
336
+ ),
337
+ cell: ({ row }) => (
338
+ <CheckBox
339
+ checked={row.getIsSelected()}
340
+ indeterminate={row.getIsSomeSelected()}
341
+ onChange={(e) => row.toggleSelected()}
342
+ />
343
+ ),
344
+ });
345
+ }
346
+
347
+ if (columns.length > 0) {
348
+ columnDefs = [
349
+ ...columnDefs,
350
+ ...columns.map((column) => {
351
+ return columnHelper.accessor(column.dataField, {
352
+ id: column.dataField,
353
+ header: () => <>{column.caption || column.dataField || ""}</>,
354
+ cell: (info) => {
355
+ if (column.cellRender) return column.cellRender(info.getValue());
356
+ const value = info.getValue();
357
+ // if value is a date
358
+ if (isValidTimestamp(value)) {
359
+ return monolithMoment({
360
+ timestamp: value,
361
+ includeTime: true,
362
+ });
363
+ }
364
+ return value;
365
+ },
366
+ filterFn: (row, columnId, filterValue) => {
367
+ if (filterValue.length === 0) return true;
368
+ return filterValue.includes(row.getValue(columnId));
369
+ },
370
+ // sortingFn: (a, b) => {
371
+ // const aDate = a?.length > 5 && moment(a).isValid();
372
+ // const bDate = b?.length > 5 && moment(b).isValid();
373
+ // if (aDate && bDate) {
374
+ // return moment(a).toDate() - moment(b).toDate();
375
+ // }
376
+ // return a - b;
377
+ // },
378
+ });
379
+ }),
380
+ ];
381
+ } else {
382
+ const keys = Object.keys(data[0]);
383
+ columnDefs = keys.map((key) => {
384
+ return columnHelper.accessor(key, {
385
+ id: key,
386
+ header: () => <>{key}</>,
387
+ cell: (info) => {
388
+ const value = info.getValue();
389
+ // if value is a date
390
+ if (isValidTimestamp(value)) {
391
+ return monolithMoment({ timestamp: value, includeTime: true });
392
+ } else return value;
393
+ },
394
+ // sortingFn: (a, b) => {
395
+ // const aDate = a?.length > 5 && moment(a).isValid();
396
+ // const bDate = b?.length > 5 && moment(b).isValid();
397
+ // if (aDate && bDate) {
398
+ // return moment(a).toDate() - moment(b).toDate();
399
+ // }
400
+ // return a - b;
401
+ // },
402
+ });
403
+ });
404
+ }
405
+
406
+ return columnDefs;
407
+ };
408
+
409
+ const FilterButton = styled(({ children, className, ...props }) => {
410
+ return (
411
+ <div className={className}>
412
+ <FilterListIcon
413
+ className="filter-icon"
414
+ onClick={(e) => {
415
+ e.stopPropagation();
416
+ props.onClick(e);
417
+ }}
418
+ />
419
+ </div>
420
+ );
421
+ })`
422
+ // outline: 1px solid red;
423
+ padding: 0px;
424
+ border-radius: 3px;
425
+ .filter-icon {
426
+ font-size: 15px;
427
+ &:hover {
428
+ cursor: pointer;
429
+ // background-color: ${({ theme }) => theme.palette.action.hover};
430
+ }
431
+ `;
432
+
433
+ const ColumnFilter = styled(({ children, className, ...props }) => {
434
+ const [anchorEl, setAnchorEl] = useState(null);
435
+ const [filterValue, setFilterValue] = useState([]);
436
+ const open = Boolean(anchorEl);
437
+
438
+ if (props.header.column.getCanFilter() === false) return null;
439
+
440
+ const dataList = useMemo(() => {
441
+ return Array.from(props.header.column.getFacetedUniqueValues().keys())
442
+ .filter((item) => !!item)
443
+ .sort();
444
+ }, [props.header.column.getFacetedUniqueValues()]);
445
+
446
+ const handleClick = (event) => {
447
+ event.stopPropagation();
448
+ setAnchorEl(event.target);
449
+ };
450
+
451
+ const handleClose = () => {
452
+ setAnchorEl(null);
453
+ };
454
+
455
+ const handleSelect = (event) => {
456
+ setFilterValue((prev) => {
457
+ let newState;
458
+ if (prev.includes(event)) {
459
+ newState = prev.filter((item) => item !== event);
460
+ } else {
461
+ newState = [...prev, event];
462
+ }
463
+ props.header.column.setFilterValue(newState);
464
+ return newState;
465
+ });
466
+ };
467
+
468
+ useEffect(() => {
469
+ const resetFilter = () => setFilterValue([]);
470
+
471
+ document.addEventListener("clearFilters", resetFilter);
472
+ return () => {
473
+ document.removeEventListener("clearFilters", resetFilter);
474
+ };
475
+ }, []);
476
+
477
+ return (
478
+ <div className={className}>
479
+ <FilterButton onClick={handleClick} />
480
+ <Menu
481
+ anchorEl={anchorEl}
482
+ open={open}
483
+ onClose={handleClose}
484
+ onItemSelect={handleSelect}
485
+ dropDownStyle={{ padding: 10 }}
486
+ >
487
+ {dataList.map((item) => {
488
+ const isChecked = filterValue.includes(item);
489
+ return (
490
+ <MenuItem
491
+ key={item}
492
+ onClick={handleClose}
493
+ data={item}
494
+ style={{
495
+ margin: "5px 0px",
496
+ display: "flex",
497
+ alignItems: "center",
498
+ }}
499
+ >
500
+ <div>
501
+ <CheckBox
502
+ checked={isChecked}
503
+ onChange={() => handleSelect(item)}
504
+ style={{ marginRight: 5 }}
505
+ />
506
+ </div>
507
+ <div>{item}</div>
508
+ </MenuItem>
509
+ );
510
+ })}
511
+ </Menu>
512
+ </div>
513
+ );
514
+ })``;
515
+
516
+ const TableContainer = styled.div`
517
+ max-height: calc(100vh - 220px);
518
+ overflow-y: auto;
519
+ // max-width: fit-content;
520
+ max-width: ${({ width }) => width || "100%"};
521
+ // padding: 3px;
522
+ ::-webkit-scrollbar-corner {
523
+ background-color: transparent;
524
+ }
525
+ `;
526
+
527
+ const Table = styled(({ children, ...props }) => {
528
+ return <table {...props}>{children}</table>;
529
+ })`
530
+ border-collapse: separate;
531
+ border-spacing: 0;
532
+ min-width: max-content;
533
+ width: 100%;
534
+ font-size: 10px;
535
+ th {
536
+ border-top: 1px solid ${(props) => props.theme.palette.divider};
537
+ border-bottom: 1px solid ${(props) => props.theme.palette.divider};
538
+ // border-right: 1px solid ${(props) => props.theme.palette.divider};
539
+ min-width: 100px;
540
+ padding: 10px 25px;
541
+ text-align: left;
542
+ overflow-x: hidden;
543
+ text-overflow: ellipsis;
544
+ white-space: nowrap;
545
+ }
546
+ th:first-child,
547
+ td:first-child {
548
+ border-left: 1px solid ${(props) => props.theme.palette.divider};
549
+ }
550
+ th:last-child,
551
+ td:last-child {
552
+ border-right: 1px solid ${(props) => props.theme.palette.divider};
553
+ }
554
+ td {
555
+ min-width: 100px;
556
+ padding: 10px;
557
+ border-bottom: 1px solid ${(props) => props.theme.palette.divider};
558
+ // border-right: 1px solid ${(props) => props.theme.palette.divider};
559
+ overflow-x: hidden;
560
+ text-overflow: ellipsis;
561
+ white-space: nowrap;
562
+ }
563
+ `;
564
+
565
+ Table.displayName = "Table";
566
+
567
+ const TableHead = styled(({ children, ...props }) => {
568
+ return <thead {...props}>{children}</thead>;
569
+ })`
570
+ user-select: none;
571
+ `;
572
+
573
+ TableHead.displayName = "TableHead";
574
+
575
+ const TableBody = styled(({ children, ...props }) => {
576
+ return <tbody {...props}>{children}</tbody>;
577
+ })((props) => ({
578
+ height: props.height,
579
+ }));
580
+
581
+ TableBody.displayName = "TableBody";
582
+
583
+ const TableHeaderRow = styled(({ className, children, row }) => {
584
+ return <tr className={className}>{children}</tr>;
585
+ })`
586
+ position: sticky;
587
+ top: 0;
588
+ border: 1px solid ${(props) => props.theme.palette.divider};
589
+ `;
590
+
591
+ TableHeaderRow.displayName = "TableHeaderRow";
592
+
593
+ const TableHeaderCell = styled(
594
+ ({ className, header, table, children, ...props }) => {
595
+ if (header.column.columnDef.columnType === "select") {
596
+ return <th className={className + " select"}>{children}</th>;
597
+ }
598
+
599
+ return (
600
+ <th
601
+ className={className}
602
+ onClick={header.column.getToggleSortingHandler()}
603
+ >
604
+ <div className="th-container">
605
+ <div>{children}</div>
606
+ {{
607
+ asc: <NorthIcon style={{ fontSize: 12, marginLeft: 5 }} />,
608
+ desc: <SouthIcon style={{ fontSize: 12, marginLeft: 5 }} />,
609
+ }[header.column.getIsSorted()] ?? (
610
+ <div style={{ width: 12, fontSize: 12, marginLeft: 5 }}></div>
611
+ )}
612
+ <div className="th-actions">
613
+ <ColumnFilter
614
+ header={header}
615
+ table={table}
616
+ columnFilters={props.columnFilters}
617
+ />
618
+ </div>
619
+ <div
620
+ className={`resizer ${
621
+ header.column.getIsResizing() ? "isResizing" : ""
622
+ }`}
623
+ onMouseDown={header.getResizeHandler()}
624
+ onClick={(e) => e.stopPropagation()}
625
+ onTouchStart={header.getResizeHandler()}
626
+ ></div>
627
+ </div>
628
+ </th>
629
+ );
630
+ }
631
+ )`
632
+ background-color: ${(props) => props.theme.palette.background.default};
633
+ cursor: pointer;
634
+ position: relative;
635
+ width: ${(props) => props.header.getSize()}px;
636
+ .th-container {
637
+ display: flex;
638
+ align-items: center;
639
+ // justify-content: space-between;
640
+ }
641
+ .th-actions {
642
+ display: flex;
643
+ align-items: center;
644
+ margin-left: auto;
645
+ // justify-content: space-between;
646
+ }
647
+ &.select {
648
+ max-width: 50px;
649
+ min-width: 50px;
650
+ padding-left: 10px;
651
+ }
652
+ &:hover {
653
+ background-color: ${(props) => props.theme.palette.dataGrid.hover};
654
+ }
655
+ .resizer {
656
+ position: absolute;
657
+ right: 0;
658
+ top: 0;
659
+ height: 100%;
660
+ width: 5px;
661
+ background-color: ${(props) => props.theme.palette.divider};
662
+ cursor: col-resize;
663
+ user-select: none;
664
+ touch-action: none;
665
+ opacity: 0;
666
+ &.isResizing {
667
+ opacity: 1;
668
+ background: ${(props) => props.theme.palette.primary.main};
669
+ }
670
+ }
671
+ `;
672
+
673
+ TableHeaderCell.displayName = "TableHeaderCell";
674
+
675
+ const TableRow = styled(({ className, children, row }) => {
676
+ const rowRef = useRef(null);
677
+ const handleRowClick = (e) => {
678
+ if (rowRef.current.classList.contains("selected")) {
679
+ rowRef.current.classList.remove("selected");
680
+ return;
681
+ }
682
+ document.querySelector("tr.selected")?.classList.remove("selected");
683
+
684
+ rowRef.current.classList.add("selected");
685
+ };
686
+
687
+ return (
688
+ <tr ref={rowRef} className={className} onClick={handleRowClick}>
689
+ {children}
690
+ </tr>
691
+ );
692
+ })((props) => ({
693
+ cursor: "pointer",
694
+ // change background color when row hovered
695
+ "&:hover": {
696
+ backgroundColor: props.theme.palette.dataGrid.hover,
697
+ },
698
+ "&.selected": {
699
+ backgroundColor: props.theme.palette.dataGrid.hover,
700
+ },
701
+ }));
702
+
703
+ TableRow.displayName = "TableRow";
704
+
705
+ const TableCell = styled(({ className, children, ...props }) => {
706
+ const isSelectCell = props.cell.column.columnDef.columnType === "select";
707
+ const rowSelected = isSelectCell && props.row.getIsSelected();
708
+
709
+ return (
710
+ <>
711
+ {useMemo(() => {
712
+ return (
713
+ <td className={className + `${isSelectCell ? " select" : ""}`}>
714
+ {children}
715
+ </td>
716
+ );
717
+ }, [rowSelected])}
718
+ </>
719
+ );
720
+ })`
721
+ &.select {
722
+ max-width: 50px;
723
+ min-width: 50px;
724
+ }
725
+ `;
726
+
727
+ TableCell.displayName = "TableCell";
728
+
729
+ export default DataGrid;