mui-table-2026 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,171 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+ import clsx from 'clsx';
3
+ import MuiTableHead from '@mui/material/TableHead';
4
+ import React, { useState } from 'react';
5
+ import TableHeadCell from './TableHeadCell';
6
+ import TableHeadRow from './TableHeadRow';
7
+ import TableSelectCell from './TableSelectCell';
8
+
9
+ const useStyles = makeStyles({ name: 'MUIDataTableHead' })((theme) => ({
10
+ main: {},
11
+ responsiveStacked: {
12
+ [theme.breakpoints.down('md')]: {
13
+ display: 'none',
14
+ },
15
+ },
16
+ responsiveStackedAlways: {
17
+ display: 'none',
18
+ },
19
+ responsiveSimple: {
20
+ [theme.breakpoints.down('sm')]: {
21
+ display: 'none',
22
+ },
23
+ },
24
+ }));
25
+
26
+ const TableHead = ({
27
+ columnOrder = null,
28
+ columns,
29
+ components = {},
30
+ count,
31
+ data,
32
+ draggableHeadCellRefs,
33
+ expandedRows,
34
+ options,
35
+ selectedRows,
36
+ selectRowUpdate,
37
+ setCellRef,
38
+ sortOrder = {},
39
+ tableRef,
40
+ tableId,
41
+ timers,
42
+ toggleAllExpandableRows,
43
+ toggleSort,
44
+ updateColumnOrder,
45
+ }) => {
46
+ const { classes } = useStyles();
47
+
48
+ if (columnOrder === null) {
49
+ columnOrder = columns ? columns.map((item, idx) => idx) : [];
50
+ }
51
+
52
+ const [dragging, setDragging] = useState(false);
53
+
54
+ const handleToggleColumn = (index) => {
55
+ toggleSort(index);
56
+ };
57
+
58
+ const handleRowSelect = () => {
59
+ selectRowUpdate('head', null);
60
+ };
61
+
62
+ const numSelected = (selectedRows && selectedRows.data.length) || 0;
63
+ let isIndeterminate = numSelected > 0 && numSelected < count;
64
+ let isChecked = numSelected > 0 && numSelected >= count;
65
+
66
+ // When the disableToolbarSelect option is true, there can be
67
+ // selected items that aren't visible, so we need to be more
68
+ // precise when determining if the head checkbox should be checked.
69
+ if (
70
+ options.disableToolbarSelect === true ||
71
+ options.selectToolbarPlacement === 'none' ||
72
+ options.selectToolbarPlacement === 'above'
73
+ ) {
74
+ if (isChecked) {
75
+ for (let ii = 0; ii < data.length; ii++) {
76
+ if (!selectedRows.lookup[data[ii].dataIndex]) {
77
+ isChecked = false;
78
+ isIndeterminate = true;
79
+ break;
80
+ }
81
+ }
82
+ } else {
83
+ if (numSelected > count) {
84
+ isIndeterminate = true;
85
+ }
86
+ }
87
+ }
88
+
89
+ let orderedColumns = columnOrder.map((colIndex, idx) => {
90
+ return {
91
+ column: columns[colIndex],
92
+ index: colIndex,
93
+ colPos: idx,
94
+ };
95
+ });
96
+
97
+ return (
98
+ <MuiTableHead
99
+ className={clsx({
100
+ [classes.responsiveStacked]:
101
+ options.responsive === 'vertical' ||
102
+ options.responsive === 'stacked' ||
103
+ options.responsive === 'stackedFullWidth',
104
+ [classes.responsiveStackedAlways]: options.responsive === 'verticalAlways',
105
+ [classes.responsiveSimple]: options.responsive === 'simple',
106
+ [classes.main]: true,
107
+ })}
108
+ >
109
+ <TableHeadRow>
110
+ <TableSelectCell
111
+ setHeadCellRef={setCellRef}
112
+ onChange={handleRowSelect.bind(null)}
113
+ indeterminate={isIndeterminate}
114
+ checked={isChecked}
115
+ isHeaderCell={true}
116
+ expandedRows={expandedRows}
117
+ expandableRowsHeader={options.expandableRowsHeader}
118
+ expandableOn={options.expandableRows}
119
+ selectableOn={options.selectableRows}
120
+ fixedHeader={options.fixedHeader}
121
+ fixedSelectColumn={options.fixedSelectColumn}
122
+ selectableRowsHeader={options.selectableRowsHeader}
123
+ selectableRowsHideCheckboxes={options.selectableRowsHideCheckboxes}
124
+ onExpand={toggleAllExpandableRows}
125
+ isRowSelectable={true}
126
+ components={components}
127
+ />
128
+ {orderedColumns.map(
129
+ ({ column, index, colPos }) =>
130
+ column.display === 'true' &&
131
+ (column.customHeadRender ? (
132
+ column.customHeadRender({ index, ...column }, handleToggleColumn, sortOrder)
133
+ ) : (
134
+ <TableHeadCell
135
+ cellHeaderProps={
136
+ columns[index].setCellHeaderProps ? columns[index].setCellHeaderProps({ index, ...column }) || {} : {}
137
+ }
138
+ key={index}
139
+ index={index}
140
+ colPosition={colPos}
141
+ type={'cell'}
142
+ setCellRef={setCellRef}
143
+ sort={column.sort}
144
+ sortDirection={column.name === sortOrder.name ? sortOrder.direction : 'none'}
145
+ toggleSort={handleToggleColumn}
146
+ hint={column.hint}
147
+ print={column.print}
148
+ options={options}
149
+ column={column}
150
+ columns={columns}
151
+ updateColumnOrder={updateColumnOrder}
152
+ columnOrder={columnOrder}
153
+ timers={timers}
154
+ draggingHook={[dragging, setDragging]}
155
+ draggableHeadCellRefs={draggableHeadCellRefs}
156
+ tableRef={tableRef}
157
+ tableId={tableId}
158
+ components={components}
159
+ >
160
+ {column.customHeadLabelRender
161
+ ? column.customHeadLabelRender({ index, colPos, ...column })
162
+ : column.label}
163
+ </TableHeadCell>
164
+ )),
165
+ )}
166
+ </TableHeadRow>
167
+ </MuiTableHead>
168
+ );
169
+ };
170
+
171
+ export default TableHead;
@@ -0,0 +1,320 @@
1
+ import Button from '@mui/material/Button';
2
+ import clsx from 'clsx';
3
+ import HelpIcon from '@mui/icons-material/Help';
4
+ import MuiTooltip from '@mui/material/Tooltip';
5
+ import PropTypes from 'prop-types';
6
+ import React, { useState } from 'react';
7
+ import TableCell from '@mui/material/TableCell';
8
+ import TableSortLabel from '@mui/material/TableSortLabel';
9
+ import useColumnDrop from '../hooks/useColumnDrop.js';
10
+ import { makeStyles } from 'tss-react/mui';
11
+ import { useDrag } from 'react-dnd';
12
+
13
+ const useStyles = makeStyles({ name: 'MUIDataTableHeadCell' })((theme) => ({
14
+ root: {
15
+ display: 'table-cell',
16
+ textAlign: 'center',
17
+ verticalAlign: 'middle',
18
+ border: `1px solid ${theme.palette.divider || '#e5e7eb'}`,
19
+ borderTop: 'none',
20
+ padding: theme.spacing(0.1, 0.25),
21
+ height: '30px',
22
+ minHeight: '30px',
23
+ maxHeight: '30px',
24
+ overflow: 'hidden',
25
+ textOverflow: 'ellipsis',
26
+ whiteSpace: 'nowrap',
27
+ backgroundColor: '#ffffff',
28
+ color: theme.palette.text.primary || '#1e293b',
29
+ fontWeight: 600,
30
+ fontSize: '15px',
31
+ fontFamily: 'Inter, system-ui, sans-serif',
32
+ },
33
+ fixedHeader: {
34
+ position: 'sticky',
35
+ top: '0px',
36
+ zIndex: 100,
37
+ backgroundColor: '#ffffff',
38
+ },
39
+ tooltip: {
40
+ cursor: 'pointer',
41
+ },
42
+ mypopper: {
43
+ '&[data-x-out-of-boundaries]': {
44
+ display: 'none',
45
+ },
46
+ },
47
+ data: {
48
+ display: 'inline-block',
49
+ textAlign: 'center',
50
+ width: '100%',
51
+ color: theme.palette.text.primary || '#1e293b',
52
+ fontWeight: 600,
53
+ fontSize: '15px',
54
+ fontFamily: 'Inter, system-ui, sans-serif',
55
+ },
56
+ sortAction: {
57
+ display: 'flex',
58
+ cursor: 'pointer',
59
+ },
60
+ dragCursor: {
61
+ cursor: 'grab',
62
+ },
63
+ sortLabelRoot: {
64
+ height: '20px',
65
+ },
66
+ sortActive: {
67
+ color: theme.palette.text.primary || '#1e293b',
68
+ },
69
+ toolButton: {
70
+ textTransform: 'none',
71
+ marginLeft: 0,
72
+ minWidth: 0,
73
+ marginRight: 0,
74
+ paddingLeft: theme.spacing(1),
75
+ paddingRight: theme.spacing(1),
76
+ textAlign: 'center',
77
+ justifyContent: 'center',
78
+ width: '100%',
79
+ color: theme.palette.text.primary || '#1e293b',
80
+ fontWeight: 600,
81
+ fontSize: '15px',
82
+ fontFamily: 'Inter, system-ui, sans-serif',
83
+ },
84
+ contentWrapper: {
85
+ display: 'flex',
86
+ alignItems: 'center',
87
+ },
88
+ hintIconAlone: {
89
+ marginTop: theme.spacing(-0.375),
90
+ marginLeft: theme.spacing(0.375),
91
+ },
92
+ hintIconWithSortIcon: {
93
+ marginTop: theme.spacing(-0.375),
94
+ },
95
+ }));
96
+
97
+ const TableHeadCell = ({
98
+ cellHeaderProps = {},
99
+ children,
100
+ colPosition,
101
+ column,
102
+ columns,
103
+ columnOrder = [],
104
+ components = {},
105
+ draggableHeadCellRefs,
106
+ draggingHook,
107
+ hint,
108
+ index,
109
+ options,
110
+ print,
111
+ setCellRef,
112
+ sort,
113
+ sortDirection,
114
+ tableRef,
115
+ tableId,
116
+ timers,
117
+ toggleSort,
118
+ updateColumnOrder,
119
+ }) => {
120
+ const [sortTooltipOpen, setSortTooltipOpen] = useState(false);
121
+ const [hintTooltipOpen, setHintTooltipOpen] = useState(false);
122
+
123
+ const { classes } = useStyles();
124
+
125
+ const handleKeyboardSortInput = (e) => {
126
+ if (e.key === 'Enter') {
127
+ toggleSort(index);
128
+ }
129
+
130
+ return false;
131
+ };
132
+
133
+ const handleSortClick = () => {
134
+ toggleSort(index);
135
+ };
136
+
137
+ const [dragging, setDragging] = draggingHook ? draggingHook : [];
138
+
139
+ const { className, ...otherProps } = cellHeaderProps;
140
+ const Tooltip = components.Tooltip || MuiTooltip;
141
+ const sortActive = sortDirection !== 'none' && sortDirection !== undefined;
142
+ const ariaSortDirection = sortDirection === 'none' ? false : sortDirection;
143
+
144
+ const isDraggingEnabled = () => {
145
+ if (!draggingHook) return false;
146
+ return options.draggableColumns && options.draggableColumns.enabled && column.draggable !== false;
147
+ };
148
+
149
+ const sortLabelProps = {
150
+ classes: { root: classes.sortLabelRoot },
151
+ tabIndex: -1,
152
+ active: sortActive,
153
+ hideSortIcon: true,
154
+ ...(ariaSortDirection ? { direction: sortDirection } : {}),
155
+ };
156
+
157
+ const [{ opacity }, dragRef, preview] = useDrag({
158
+ type: 'HEADER',
159
+ item: () => {
160
+ setTimeout(() => {
161
+ setHintTooltipOpen(false);
162
+ setSortTooltipOpen(false);
163
+ setDragging(true);
164
+ }, 0);
165
+ return {
166
+ colIndex: index,
167
+ headCellRefs: draggableHeadCellRefs,
168
+ };
169
+ },
170
+ end: (item, monitor) => {
171
+ setDragging(false);
172
+ },
173
+ collect: (monitor) => {
174
+ return {
175
+ opacity: monitor.isDragging() ? 1 : 0,
176
+ };
177
+ },
178
+ });
179
+
180
+ const [drop] = useColumnDrop({
181
+ drop: (item, mon) => {
182
+ setSortTooltipOpen(false);
183
+ setHintTooltipOpen(false);
184
+ setDragging(false);
185
+ },
186
+ index,
187
+ headCellRefs: draggableHeadCellRefs,
188
+ updateColumnOrder,
189
+ columnOrder,
190
+ columns,
191
+ transitionTime: options.draggableColumns ? options.draggableColumns.transitionTime : 300,
192
+ tableRef: tableRef ? tableRef() : null,
193
+ tableId: tableId || 'none',
194
+ timers,
195
+ });
196
+
197
+ const cellClass = clsx({
198
+ [classes.root]: true,
199
+ [classes.fixedHeader]: options.fixedHeader,
200
+ 'datatables-noprint': !print,
201
+ [className]: className,
202
+ });
203
+
204
+ const showHintTooltip = () => {
205
+ setSortTooltipOpen(false);
206
+ setHintTooltipOpen(true);
207
+ };
208
+
209
+ const getTooltipTitle = () => {
210
+ if (dragging) return '';
211
+ if (!options.textLabels) return '';
212
+ return options.textLabels.body.columnHeaderTooltip
213
+ ? options.textLabels.body.columnHeaderTooltip(column)
214
+ : options.textLabels.body.toolTip;
215
+ };
216
+
217
+ const closeTooltip = () => {
218
+ setSortTooltipOpen(false);
219
+ };
220
+
221
+ return (
222
+ <TableCell
223
+ ref={(ref) => {
224
+ drop && drop(ref);
225
+ setCellRef && setCellRef(index + 1, colPosition + 1, ref);
226
+ }}
227
+ className={cellClass}
228
+ scope={'col'}
229
+ sortDirection={ariaSortDirection}
230
+ data-colindex={index}
231
+ data-tableid={tableId}
232
+ onMouseDown={closeTooltip}
233
+ {...otherProps}>
234
+ {options.sort && sort ? (
235
+ <span className={classes.contentWrapper}>
236
+ <Tooltip
237
+ title={getTooltipTitle()}
238
+ placement="bottom"
239
+ open={sortTooltipOpen}
240
+ onOpen={() => (dragging ? setSortTooltipOpen(false) : setSortTooltipOpen(true))}
241
+ onClose={() => setSortTooltipOpen(false)}
242
+ classes={{
243
+ tooltip: classes.tooltip,
244
+ popper: classes.mypopper,
245
+ }}>
246
+ <Button
247
+ variant=""
248
+ onKeyUp={handleKeyboardSortInput}
249
+ onClick={handleSortClick}
250
+ className={classes.toolButton}
251
+ data-testid={`headcol-${index}`}
252
+ ref={isDraggingEnabled() ? dragRef : null}>
253
+ <div className={classes.sortAction}>
254
+ <div
255
+ className={clsx({
256
+ [classes.data]: true,
257
+ [classes.sortActive]: sortActive,
258
+ [classes.dragCursor]: isDraggingEnabled(),
259
+ })}>
260
+ {children}
261
+ </div>
262
+ <div className={classes.sortAction}>
263
+ <TableSortLabel {...sortLabelProps} />
264
+ </div>
265
+ </div>
266
+ </Button>
267
+ </Tooltip>
268
+ {hint && (
269
+ <Tooltip title={hint}>
270
+ <HelpIcon
271
+ className={!sortActive ? classes.hintIconAlone : classes.hintIconWithSortIcon}
272
+ fontSize="small"
273
+ />
274
+ </Tooltip>
275
+ )}
276
+ </span>
277
+ ) : (
278
+ <div className={hint ? classes.sortAction : null} ref={isDraggingEnabled() ? dragRef : null}>
279
+ {children}
280
+ {hint && (
281
+ <Tooltip
282
+ title={hint}
283
+ placement={'bottom-end'}
284
+ open={hintTooltipOpen}
285
+ onOpen={() => showHintTooltip()}
286
+ onClose={() => setHintTooltipOpen(false)}
287
+ classes={{
288
+ tooltip: classes.tooltip,
289
+ popper: classes.mypopper,
290
+ }}
291
+ enterDelay={300}>
292
+ <HelpIcon className={classes.hintIconAlone} fontSize="small" />
293
+ </Tooltip>
294
+ )}
295
+ </div>
296
+ )}
297
+ </TableCell>
298
+ );
299
+ };
300
+
301
+ TableHeadCell.propTypes = {
302
+ /** Options used to describe table */
303
+ options: PropTypes.object.isRequired,
304
+ /** Current sort direction */
305
+ sortDirection: PropTypes.oneOf(['asc', 'desc', 'none']),
306
+ /** Callback to trigger column sort */
307
+ toggleSort: PropTypes.func.isRequired,
308
+ /** Sort enabled / disabled for this column **/
309
+ sort: PropTypes.bool.isRequired,
310
+ /** Hint tooltip text */
311
+ hint: PropTypes.string,
312
+ /** Column displayed in print */
313
+ print: PropTypes.bool.isRequired,
314
+ /** Optional to be used with `textLabels.body.columnHeaderTooltip` */
315
+ column: PropTypes.object,
316
+ /** Injectable component structure **/
317
+ components: PropTypes.object,
318
+ };
319
+
320
+ export default TableHeadCell;
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import clsx from 'clsx';
4
+ import TableRow from '@mui/material/TableRow';
5
+ import { makeStyles } from 'tss-react/mui';
6
+
7
+ const useStyles = makeStyles({ name: 'MUIDataTableHeadRow' })(() => ({
8
+ root: {},
9
+ }));
10
+
11
+ const TableHeadRow = ({ children }) => {
12
+ const { classes } = useStyles();
13
+
14
+ return (
15
+ <TableRow
16
+ className={clsx({
17
+ [classes.root]: true,
18
+ })}
19
+ >
20
+ {children}
21
+ </TableRow>
22
+ );
23
+ };
24
+
25
+ TableHeadRow.propTypes = {
26
+ children: PropTypes.node,
27
+ };
28
+
29
+ export default TableHeadRow;
@@ -0,0 +1,152 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import MuiTableCell from '@mui/material/TableCell';
4
+ import MuiTableRow from '@mui/material/TableRow';
5
+ import MuiTableFooter from '@mui/material/TableFooter';
6
+ import MuiTablePagination from '@mui/material/TablePagination';
7
+ import JumpToPage from './JumpToPage';
8
+ import { makeStyles } from 'tss-react/mui';
9
+ import { getPageValue } from '../utils';
10
+
11
+ const useStyles = makeStyles({ name: 'MUIDataTablePagination' })((theme) => ({
12
+ root: {},
13
+ tableCellContainer: {
14
+ padding: '6px 12px',
15
+ backgroundColor: theme.palette.background.paper || '#ffffff',
16
+ },
17
+ navContainer: {
18
+ display: 'flex',
19
+ justifyContent: 'flex-end',
20
+ alignItems: 'center',
21
+ },
22
+ toolbar: {
23
+ display: 'flex',
24
+ alignItems: 'center',
25
+ gap: theme.spacing(1),
26
+ },
27
+ selectRoot: {
28
+ backgroundColor: '#f8fafc',
29
+ borderRadius: theme.shape.borderRadius || '4px',
30
+ border: `1px solid ${theme.palette.divider || '#e5e7eb'}`,
31
+ '& .MuiSelect-select': {
32
+ padding: '0px 2px',
33
+ fontSize: '12px',
34
+ display: 'flex',
35
+ alignItems: 'center',
36
+ justifyContent: 'space-between',
37
+ minHeight: '20px',
38
+ },
39
+ '& .MuiSelect-icon': {
40
+ color: theme.palette.text.secondary || '#6b7280',
41
+ backgroundColor: '#f8fafc',
42
+ marginRight: '2px',
43
+ },
44
+ '&:hover': {
45
+ borderColor: theme.palette.primary.main || '#3b82f6',
46
+ },
47
+ '&.Mui-focused': {
48
+ borderColor: theme.palette.primary.main || '#3b82f6',
49
+ boxShadow: `0 0 0 2px ${theme.palette.primary.main}20`,
50
+ },
51
+ },
52
+ '@media screen and (max-width: 400px)': {
53
+ toolbar: {
54
+ '& span:nth-of-type(2)': {
55
+ display: 'none',
56
+ },
57
+ },
58
+ selectRoot: {
59
+ marginRight: '8px',
60
+ },
61
+ },
62
+ }));
63
+
64
+ function TablePagination(props) {
65
+ const { classes } = useStyles();
66
+
67
+ const handleRowChange = (event) => {
68
+ props.changeRowsPerPage(event.target.value);
69
+ };
70
+
71
+ const handlePageChange = (_, page) => {
72
+ props.changePage(page);
73
+ };
74
+
75
+ const { count, options, rowsPerPage, page } = props;
76
+ const textLabels = options.textLabels.pagination;
77
+
78
+ return (
79
+ <MuiTableFooter>
80
+ <MuiTableRow>
81
+ <MuiTableCell colSpan="1000" className={classes.tableCellContainer}>
82
+ <div className={classes.navContainer}>
83
+ {options.jumpToPage ? (
84
+ <JumpToPage
85
+ count={count}
86
+ page={page}
87
+ rowsPerPage={rowsPerPage}
88
+ textLabels={options.textLabels}
89
+ changePage={props.changePage}
90
+ changeRowsPerPage={props.changeRowsPerPage}
91
+ />
92
+ ) : null}
93
+ <MuiTablePagination
94
+ component="div"
95
+ className={classes.root}
96
+ classes={{
97
+ caption: classes.caption,
98
+ toolbar: classes.toolbar,
99
+ selectRoot: classes.selectRoot,
100
+ }}
101
+ count={count}
102
+ rowsPerPage={rowsPerPage}
103
+ page={getPageValue(count, rowsPerPage, page)}
104
+ labelRowsPerPage={textLabels.rowsPerPage}
105
+ labelDisplayedRows={({ from, to, count }) => `${from}-${to} ${textLabels.displayRows} ${count}`}
106
+ backIconButtonProps={{
107
+ id: 'pagination-back',
108
+ 'data-testid': 'pagination-back',
109
+ 'aria-label': textLabels.previous,
110
+ title: textLabels.previous || '',
111
+ }}
112
+ nextIconButtonProps={{
113
+ id: 'pagination-next',
114
+ 'data-testid': 'pagination-next',
115
+ 'aria-label': textLabels.next,
116
+ title: textLabels.next || '',
117
+ }}
118
+ SelectProps={{
119
+ id: 'pagination-input',
120
+ variant: 'standard',
121
+ SelectDisplayProps: { id: 'pagination-rows', 'data-testid': 'pagination-rows' },
122
+ MenuProps: {
123
+ id: 'pagination-menu',
124
+ 'data-testid': 'pagination-menu',
125
+ MenuListProps: { id: 'pagination-menu-list', 'data-testid': 'pagination-menu-list' },
126
+ },
127
+ }}
128
+ rowsPerPageOptions={options.rowsPerPageOptions}
129
+ onPageChange={handlePageChange}
130
+ onRowsPerPageChange={handleRowChange}
131
+ />
132
+ </div>
133
+ </MuiTableCell>
134
+ </MuiTableRow>
135
+ </MuiTableFooter>
136
+ );
137
+ }
138
+
139
+ TablePagination.propTypes = {
140
+ /** Total number of table rows */
141
+ count: PropTypes.number.isRequired,
142
+ /** Options used to describe table */
143
+ options: PropTypes.object.isRequired,
144
+ /** Current page index */
145
+ page: PropTypes.number.isRequired,
146
+ /** Total number allowed of rows per page */
147
+ rowsPerPage: PropTypes.number.isRequired,
148
+ /** Callback to trigger rows per page change */
149
+ changeRowsPerPage: PropTypes.func.isRequired,
150
+ };
151
+
152
+ export default TablePagination;