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.
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/package.json +115 -0
- package/src/MUIDataTable.js +2107 -0
- package/src/components/ExpandButton.js +39 -0
- package/src/components/JumpToPage.js +96 -0
- package/src/components/Popover.js +89 -0
- package/src/components/TableBody.js +338 -0
- package/src/components/TableBodyCell.js +276 -0
- package/src/components/TableBodyRow.js +98 -0
- package/src/components/TableFilter.js +421 -0
- package/src/components/TableFilterList.js +136 -0
- package/src/components/TableFilterListItem.js +20 -0
- package/src/components/TableFooter.js +74 -0
- package/src/components/TableHead.js +171 -0
- package/src/components/TableHeadCell.js +320 -0
- package/src/components/TableHeadRow.js +29 -0
- package/src/components/TablePagination.js +152 -0
- package/src/components/TableResize.js +288 -0
- package/src/components/TableSearch.js +89 -0
- package/src/components/TableSelectCell.js +163 -0
- package/src/components/TableToolbar.js +630 -0
- package/src/components/TableToolbarSelect.js +91 -0
- package/src/components/TableViewCol.js +101 -0
- package/src/hooks/useColumnDrop.js +186 -0
- package/src/index.js +44 -0
- package/src/localStorage/index.js +2 -0
- package/src/localStorage/load.js +10 -0
- package/src/localStorage/save.js +5 -0
- package/src/plug-ins/DebounceSearchRender.js +118 -0
- package/src/textLabels.js +39 -0
- package/src/utils.js +150 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { Button } from '@mui/material';
|
|
2
|
+
import { Checkbox } from '@mui/material';
|
|
3
|
+
import { FormControl } from '@mui/material';
|
|
4
|
+
import { FormControlLabel } from '@mui/material';
|
|
5
|
+
import { FormGroup } from '@mui/material';
|
|
6
|
+
import { Grid } from '@mui/material';
|
|
7
|
+
import { Input } from '@mui/material';
|
|
8
|
+
import { InputLabel } from '@mui/material';
|
|
9
|
+
import { ListItemText } from '@mui/material';
|
|
10
|
+
import { MenuItem } from '@mui/material';
|
|
11
|
+
import PropTypes from 'prop-types';
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { Select } from '@mui/material';
|
|
14
|
+
import { TextField } from '@mui/material';
|
|
15
|
+
import { Typography } from '@mui/material';
|
|
16
|
+
import clsx from 'clsx';
|
|
17
|
+
import { withStyles } from 'tss-react/mui';
|
|
18
|
+
import cloneDeep from 'lodash.clonedeep';
|
|
19
|
+
|
|
20
|
+
export const defaultFilterStyles = theme => ({
|
|
21
|
+
root: {
|
|
22
|
+
backgroundColor: theme.palette.background.default,
|
|
23
|
+
padding: '24px 24px 36px 24px',
|
|
24
|
+
fontFamily: 'Roboto',
|
|
25
|
+
},
|
|
26
|
+
header: {
|
|
27
|
+
flex: '0 0 auto',
|
|
28
|
+
marginBottom: '16px',
|
|
29
|
+
width: '100%',
|
|
30
|
+
display: 'flex',
|
|
31
|
+
justifyContent: 'space-between',
|
|
32
|
+
},
|
|
33
|
+
title: {
|
|
34
|
+
display: 'inline-block',
|
|
35
|
+
marginLeft: '7px',
|
|
36
|
+
color: theme.palette.text.primary,
|
|
37
|
+
fontSize: '14px',
|
|
38
|
+
fontWeight: 500,
|
|
39
|
+
},
|
|
40
|
+
noMargin: {
|
|
41
|
+
marginLeft: '0px',
|
|
42
|
+
},
|
|
43
|
+
reset: {
|
|
44
|
+
alignSelf: 'left',
|
|
45
|
+
},
|
|
46
|
+
resetLink: {
|
|
47
|
+
marginLeft: '16px',
|
|
48
|
+
fontSize: '12px',
|
|
49
|
+
cursor: 'pointer',
|
|
50
|
+
},
|
|
51
|
+
filtersSelected: {
|
|
52
|
+
alignSelf: 'right',
|
|
53
|
+
},
|
|
54
|
+
/* checkbox */
|
|
55
|
+
checkboxListTitle: {
|
|
56
|
+
marginLeft: '7px',
|
|
57
|
+
marginBottom: '8px',
|
|
58
|
+
fontSize: '14px',
|
|
59
|
+
color: theme.palette.text.secondary,
|
|
60
|
+
textAlign: 'left',
|
|
61
|
+
fontWeight: 500,
|
|
62
|
+
},
|
|
63
|
+
checkboxFormGroup: {
|
|
64
|
+
marginTop: '8px',
|
|
65
|
+
},
|
|
66
|
+
checkboxFormControl: {
|
|
67
|
+
margin: '0px',
|
|
68
|
+
},
|
|
69
|
+
checkboxFormControlLabel: {
|
|
70
|
+
fontSize: '15px',
|
|
71
|
+
marginLeft: '8px',
|
|
72
|
+
color: theme.palette.text.primary,
|
|
73
|
+
},
|
|
74
|
+
checkboxIcon: {
|
|
75
|
+
width: '32px',
|
|
76
|
+
height: '32px',
|
|
77
|
+
},
|
|
78
|
+
checkbox: {},
|
|
79
|
+
checked: {},
|
|
80
|
+
gridListTile: {
|
|
81
|
+
marginTop: '16px',
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
class TableFilter extends React.Component {
|
|
86
|
+
static propTypes = {
|
|
87
|
+
/** Data used to populate filter dropdown/checkbox */
|
|
88
|
+
filterData: PropTypes.array.isRequired,
|
|
89
|
+
/** Data selected to be filtered against dropdown/checkbox */
|
|
90
|
+
filterList: PropTypes.array.isRequired,
|
|
91
|
+
/** Options used to describe table */
|
|
92
|
+
options: PropTypes.object.isRequired,
|
|
93
|
+
/** Callback to trigger filter update */
|
|
94
|
+
onFilterUpdate: PropTypes.func,
|
|
95
|
+
/** Callback to trigger filter reset */
|
|
96
|
+
onFilterReset: PropTypes.func,
|
|
97
|
+
/** Extend the style applied to components */
|
|
98
|
+
classes: PropTypes.object,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
constructor(props) {
|
|
102
|
+
super(props);
|
|
103
|
+
this.state = {
|
|
104
|
+
filterList: cloneDeep(props.filterList),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
filterUpdate = (index, value, column, type, customUpdate) => {
|
|
109
|
+
let newFilterList = this.state.filterList.slice(0);
|
|
110
|
+
|
|
111
|
+
this.props.updateFilterByType(newFilterList, index, value, type, customUpdate);
|
|
112
|
+
this.setState({
|
|
113
|
+
filterList: newFilterList,
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
handleCheckboxChange = (index, value, column) => {
|
|
118
|
+
this.filterUpdate(index, value, column, 'checkbox');
|
|
119
|
+
|
|
120
|
+
if (this.props.options.confirmFilters !== true) {
|
|
121
|
+
this.props.onFilterUpdate(index, value, column, 'checkbox');
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
handleDropdownChange = (event, index, column) => {
|
|
126
|
+
const labelFilterAll = this.props.options.textLabels.filter.all;
|
|
127
|
+
const value = event.target.value === labelFilterAll ? [] : [event.target.value];
|
|
128
|
+
this.filterUpdate(index, value, column, 'dropdown');
|
|
129
|
+
|
|
130
|
+
if (this.props.options.confirmFilters !== true) {
|
|
131
|
+
this.props.onFilterUpdate(index, value, column, 'dropdown');
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
handleMultiselectChange = (index, value, column) => {
|
|
136
|
+
this.filterUpdate(index, value, column, 'multiselect');
|
|
137
|
+
|
|
138
|
+
if (this.props.options.confirmFilters !== true) {
|
|
139
|
+
this.props.onFilterUpdate(index, value, column, 'multiselect');
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
handleTextFieldChange = (event, index, column) => {
|
|
144
|
+
this.filterUpdate(index, event.target.value, column, 'textField');
|
|
145
|
+
|
|
146
|
+
if (this.props.options.confirmFilters !== true) {
|
|
147
|
+
this.props.onFilterUpdate(index, event.target.value, column, 'textField');
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
handleCustomChange = (value, index, column) => {
|
|
152
|
+
this.filterUpdate(index, value, column.name, column.filterType);
|
|
153
|
+
|
|
154
|
+
if (this.props.options.confirmFilters !== true) {
|
|
155
|
+
this.props.onFilterUpdate(index, value, column.name, column.filterType);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
renderCheckbox(column, index, components = {}) {
|
|
160
|
+
const CheckboxComponent = components.Checkbox || Checkbox;
|
|
161
|
+
|
|
162
|
+
const { classes, filterData } = this.props;
|
|
163
|
+
const { filterList } = this.state;
|
|
164
|
+
const renderItem =
|
|
165
|
+
column.filterOptions && column.filterOptions.renderValue ? column.filterOptions.renderValue : v => v;
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<Grid item key={index} xs={6}>
|
|
169
|
+
<FormGroup>
|
|
170
|
+
<Grid item xs={12}>
|
|
171
|
+
<Typography variant="body2" className={classes.checkboxListTitle}>
|
|
172
|
+
{column.label}
|
|
173
|
+
</Typography>
|
|
174
|
+
</Grid>
|
|
175
|
+
<Grid container>
|
|
176
|
+
{filterData[index].map((filterValue, filterIndex) => (
|
|
177
|
+
<Grid item key={filterIndex}>
|
|
178
|
+
<FormControlLabel
|
|
179
|
+
key={filterIndex}
|
|
180
|
+
classes={{
|
|
181
|
+
root: classes.checkboxFormControl,
|
|
182
|
+
label: classes.checkboxFormControlLabel,
|
|
183
|
+
}}
|
|
184
|
+
control={
|
|
185
|
+
<CheckboxComponent
|
|
186
|
+
data-description="table-filter"
|
|
187
|
+
color="primary"
|
|
188
|
+
className={classes.checkboxIcon}
|
|
189
|
+
onChange={this.handleCheckboxChange.bind(null, index, filterValue, column.name)}
|
|
190
|
+
checked={filterList[index].indexOf(filterValue) >= 0}
|
|
191
|
+
classes={{
|
|
192
|
+
root: classes.checkbox,
|
|
193
|
+
checked: classes.checked,
|
|
194
|
+
}}
|
|
195
|
+
value={filterValue != null ? filterValue.toString() : ''}
|
|
196
|
+
/>
|
|
197
|
+
}
|
|
198
|
+
label={renderItem(filterValue)}
|
|
199
|
+
/>
|
|
200
|
+
</Grid>
|
|
201
|
+
))}
|
|
202
|
+
</Grid>
|
|
203
|
+
</FormGroup>
|
|
204
|
+
</Grid>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
renderSelect(column, index) {
|
|
209
|
+
const { classes, filterData, options } = this.props;
|
|
210
|
+
const { filterList } = this.state;
|
|
211
|
+
const textLabels = options.textLabels.filter;
|
|
212
|
+
const renderItem =
|
|
213
|
+
column.filterOptions && column.filterOptions.renderValue
|
|
214
|
+
? column.filterOptions.renderValue
|
|
215
|
+
: v => (v != null ? v.toString() : '');
|
|
216
|
+
const width = (column.filterOptions && column.filterOptions.fullWidth) === true ? 12 : 6;
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
<Grid
|
|
220
|
+
item
|
|
221
|
+
key={index}
|
|
222
|
+
xs={width}
|
|
223
|
+
classes={{ 'grid-xs-12': classes.gridListTile, 'grid-xs-6': classes.gridListTile }}>
|
|
224
|
+
<FormControl key={index} variant={'standard'} fullWidth>
|
|
225
|
+
<InputLabel htmlFor={column.name}>{column.label}</InputLabel>
|
|
226
|
+
<Select
|
|
227
|
+
fullWidth
|
|
228
|
+
value={filterList[index].length ? filterList[index].toString() : textLabels.all}
|
|
229
|
+
name={column.name}
|
|
230
|
+
onChange={event => this.handleDropdownChange(event, index, column.name)}
|
|
231
|
+
input={<Input name={column.name} id={column.name} />}>
|
|
232
|
+
<MenuItem value={textLabels.all} key={0}>
|
|
233
|
+
{textLabels.all}
|
|
234
|
+
</MenuItem>
|
|
235
|
+
{filterData[index].map((filterValue, filterIndex) => (
|
|
236
|
+
<MenuItem value={filterValue} key={filterIndex + 1}>
|
|
237
|
+
{renderItem(filterValue)}
|
|
238
|
+
</MenuItem>
|
|
239
|
+
))}
|
|
240
|
+
</Select>
|
|
241
|
+
</FormControl>
|
|
242
|
+
</Grid>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
renderTextField(column, index) {
|
|
247
|
+
const { classes } = this.props;
|
|
248
|
+
const { filterList } = this.state;
|
|
249
|
+
if (column.filterOptions && column.filterOptions.renderValue) {
|
|
250
|
+
console.warn('Custom renderValue not supported for textField filters');
|
|
251
|
+
}
|
|
252
|
+
const width = (column.filterOptions && column.filterOptions.fullWidth) === true ? 12 : 6;
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
<Grid
|
|
256
|
+
item
|
|
257
|
+
key={index}
|
|
258
|
+
xs={width}
|
|
259
|
+
classes={{ 'grid-xs-12': classes.gridListTile, 'grid-xs-6': classes.gridListTile }}>
|
|
260
|
+
<FormControl key={index} fullWidth>
|
|
261
|
+
<TextField
|
|
262
|
+
fullWidth
|
|
263
|
+
variant={'standard'}
|
|
264
|
+
label={column.label}
|
|
265
|
+
value={filterList[index].toString() || ''}
|
|
266
|
+
data-testid={'filtertextfield-' + column.name}
|
|
267
|
+
onChange={event => this.handleTextFieldChange(event, index, column.name)}
|
|
268
|
+
/>
|
|
269
|
+
</FormControl>
|
|
270
|
+
</Grid>
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
renderMultiselect(column, index, components = {}) {
|
|
275
|
+
const CheckboxComponent = components.Checkbox || Checkbox;
|
|
276
|
+
|
|
277
|
+
const { classes, filterData } = this.props;
|
|
278
|
+
const { filterList } = this.state;
|
|
279
|
+
const renderItem =
|
|
280
|
+
column.filterOptions && column.filterOptions.renderValue ? column.filterOptions.renderValue : v => v;
|
|
281
|
+
const width = (column.filterOptions && column.filterOptions.fullWidth) === true ? 12 : 6;
|
|
282
|
+
return (
|
|
283
|
+
<Grid
|
|
284
|
+
item
|
|
285
|
+
key={index}
|
|
286
|
+
xs={width}
|
|
287
|
+
classes={{ 'grid-xs-12': classes.gridListTile, 'grid-xs-6': classes.gridListTile }}>
|
|
288
|
+
<FormControl key={index} variant={'standard'} fullWidth>
|
|
289
|
+
<InputLabel htmlFor={column.name}>{column.label}</InputLabel>
|
|
290
|
+
<Select
|
|
291
|
+
multiple
|
|
292
|
+
fullWidth
|
|
293
|
+
value={filterList[index] || []}
|
|
294
|
+
renderValue={selected => selected.map(renderItem).join(', ')}
|
|
295
|
+
name={column.name}
|
|
296
|
+
onChange={event => this.handleMultiselectChange(index, event.target.value, column.name)}
|
|
297
|
+
input={<Input name={column.name} id={column.name} />}>
|
|
298
|
+
{filterData[index].map((filterValue, filterIndex) => (
|
|
299
|
+
<MenuItem value={filterValue} key={filterIndex + 1}>
|
|
300
|
+
<CheckboxComponent
|
|
301
|
+
data-description="table-filter"
|
|
302
|
+
color="primary"
|
|
303
|
+
checked={filterList[index].indexOf(filterValue) >= 0}
|
|
304
|
+
value={filterValue != null ? filterValue.toString() : ''}
|
|
305
|
+
className={classes.checkboxIcon}
|
|
306
|
+
classes={{
|
|
307
|
+
root: classes.checkbox,
|
|
308
|
+
checked: classes.checked,
|
|
309
|
+
}}
|
|
310
|
+
/>
|
|
311
|
+
<ListItemText primary={renderItem(filterValue)} />
|
|
312
|
+
</MenuItem>
|
|
313
|
+
))}
|
|
314
|
+
</Select>
|
|
315
|
+
</FormControl>
|
|
316
|
+
</Grid>
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
renderCustomField(column, index) {
|
|
321
|
+
const { classes, filterData, options } = this.props;
|
|
322
|
+
const { filterList } = this.state;
|
|
323
|
+
const width = (column.filterOptions && column.filterOptions.fullWidth) === true ? 12 : 6;
|
|
324
|
+
const display =
|
|
325
|
+
(column.filterOptions && column.filterOptions.display) ||
|
|
326
|
+
(options.filterOptions && options.filterOptions.display);
|
|
327
|
+
|
|
328
|
+
if (!display) {
|
|
329
|
+
console.error('Property "display" is required when using custom filter type.');
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (column.filterListOptions && column.filterListOptions.renderValue) {
|
|
333
|
+
console.warning('"renderValue" is ignored for custom filter fields');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
<Grid
|
|
338
|
+
item
|
|
339
|
+
key={index}
|
|
340
|
+
xs={width}
|
|
341
|
+
classes={{ 'grid-xs-12': classes.gridListTile, 'grid-xs-6': classes.gridListTile }}>
|
|
342
|
+
<FormControl key={index} fullWidth>
|
|
343
|
+
{display(filterList, this.handleCustomChange, index, column, filterData)}
|
|
344
|
+
</FormControl>
|
|
345
|
+
</Grid>
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
applyFilters = () => {
|
|
350
|
+
this.state.filterList.forEach((filter, index) => {
|
|
351
|
+
this.props.onFilterUpdate(index, filter, this.props.columns[index], 'custom');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
this.props.handleClose(); // close filter dialog popover
|
|
355
|
+
|
|
356
|
+
if (this.props.options.onFilterConfirm) {
|
|
357
|
+
this.props.options.onFilterConfirm(this.state.filterList);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return this.state.filterList;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
resetFilters = () => {
|
|
364
|
+
this.setState({
|
|
365
|
+
filterList: this.props.columns.map(() => []),
|
|
366
|
+
});
|
|
367
|
+
if (this.props.options.confirmFilters !== true) {
|
|
368
|
+
this.props.onFilterReset();
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
render() {
|
|
373
|
+
const { classes, columns, options, customFooter, filterList, components = {} } = this.props;
|
|
374
|
+
const textLabels = options.textLabels.filter;
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<div className={classes.root}>
|
|
378
|
+
<div className={classes.header}>
|
|
379
|
+
<div className={classes.reset}>
|
|
380
|
+
<Typography
|
|
381
|
+
variant="body2"
|
|
382
|
+
className={clsx({
|
|
383
|
+
[classes.title]: true,
|
|
384
|
+
})}>
|
|
385
|
+
{textLabels.title}
|
|
386
|
+
</Typography>
|
|
387
|
+
<Button
|
|
388
|
+
color="primary"
|
|
389
|
+
className={classes.resetLink}
|
|
390
|
+
tabIndex={0}
|
|
391
|
+
aria-label={textLabels.reset}
|
|
392
|
+
data-testid={'filterReset-button'}
|
|
393
|
+
onClick={this.resetFilters}>
|
|
394
|
+
{textLabels.reset}
|
|
395
|
+
</Button>
|
|
396
|
+
</div>
|
|
397
|
+
<div className={classes.filtersSelected} />
|
|
398
|
+
</div>
|
|
399
|
+
<Grid container direction="row" justifyContent="flex-start" alignItems="center" spacing={4}>
|
|
400
|
+
{columns.map((column, index) => {
|
|
401
|
+
if (column.filter) {
|
|
402
|
+
const filterType = column.filterType || options.filterType;
|
|
403
|
+
return filterType === 'checkbox'
|
|
404
|
+
? this.renderCheckbox(column, index, components)
|
|
405
|
+
: filterType === 'multiselect'
|
|
406
|
+
? this.renderMultiselect(column, index, components)
|
|
407
|
+
: filterType === 'textField'
|
|
408
|
+
? this.renderTextField(column, index)
|
|
409
|
+
: filterType === 'custom'
|
|
410
|
+
? this.renderCustomField(column, index)
|
|
411
|
+
: this.renderSelect(column, index);
|
|
412
|
+
}
|
|
413
|
+
})}
|
|
414
|
+
</Grid>
|
|
415
|
+
{customFooter ? customFooter(filterList, this.applyFilters) : ''}
|
|
416
|
+
</div>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export default withStyles(TableFilter, defaultFilterStyles, { name: 'MUIDataTableFilter' });
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { makeStyles } from 'tss-react/mui';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import TableFilterListItem from './TableFilterListItem';
|
|
5
|
+
|
|
6
|
+
const useStyles = makeStyles({ name: 'MUIDataTableFilterList' })(() => ({
|
|
7
|
+
root: {
|
|
8
|
+
display: 'flex',
|
|
9
|
+
justifyContent: 'left',
|
|
10
|
+
flexWrap: 'wrap',
|
|
11
|
+
margin: '0px 16px 0px 16px',
|
|
12
|
+
},
|
|
13
|
+
chip: {
|
|
14
|
+
margin: '8px 8px 0px 0px',
|
|
15
|
+
},
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
const TableFilterList = ({
|
|
19
|
+
options,
|
|
20
|
+
filterList,
|
|
21
|
+
filterUpdate,
|
|
22
|
+
filterListRenderers,
|
|
23
|
+
columnNames,
|
|
24
|
+
serverSideFilterList,
|
|
25
|
+
customFilterListUpdate,
|
|
26
|
+
ItemComponent = TableFilterListItem,
|
|
27
|
+
}) => {
|
|
28
|
+
const { classes } = useStyles();
|
|
29
|
+
const { serverSide } = options;
|
|
30
|
+
|
|
31
|
+
const removeFilter = (index, filterValue, columnName, filterType, customFilterListUpdate = null) => {
|
|
32
|
+
let removedFilter = filterValue;
|
|
33
|
+
if (Array.isArray(removedFilter) && removedFilter.length === 0) {
|
|
34
|
+
removedFilter = filterList[index];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
filterUpdate(index, filterValue, columnName, filterType, customFilterListUpdate, filterList => {
|
|
38
|
+
if (options.onFilterChipClose) {
|
|
39
|
+
options.onFilterChipClose(index, removedFilter, filterList);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
const customFilterChip = (customFilterItem, index, customFilterItemIndex, item, isArray) => {
|
|
44
|
+
let type;
|
|
45
|
+
// If our custom filter list is an array, we need to check for custom update functions to determine
|
|
46
|
+
// default type. Otherwise we use the supplied type in options.
|
|
47
|
+
if (isArray) {
|
|
48
|
+
type = customFilterListUpdate[index] ? 'custom' : 'chip';
|
|
49
|
+
} else {
|
|
50
|
+
type = columnNames[index].filterType;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<ItemComponent
|
|
55
|
+
label={customFilterItem}
|
|
56
|
+
key={customFilterItemIndex}
|
|
57
|
+
onDelete={() =>
|
|
58
|
+
removeFilter(
|
|
59
|
+
index,
|
|
60
|
+
item[customFilterItemIndex] || [],
|
|
61
|
+
columnNames[index].name,
|
|
62
|
+
type,
|
|
63
|
+
customFilterListUpdate[index],
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
className={classes.chip}
|
|
67
|
+
itemKey={customFilterItemIndex}
|
|
68
|
+
index={index}
|
|
69
|
+
data={item}
|
|
70
|
+
columnNames={columnNames}
|
|
71
|
+
filterProps={
|
|
72
|
+
options.setFilterChipProps
|
|
73
|
+
? options.setFilterChipProps(index, columnNames[index].name, item[customFilterItemIndex] || [])
|
|
74
|
+
: {}
|
|
75
|
+
}
|
|
76
|
+
/>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const filterChip = (index, data, colIndex) => (
|
|
81
|
+
<ItemComponent
|
|
82
|
+
label={filterListRenderers[index](data)}
|
|
83
|
+
key={colIndex}
|
|
84
|
+
onDelete={() => removeFilter(index, data, columnNames[index].name, 'chip')}
|
|
85
|
+
className={classes.chip}
|
|
86
|
+
itemKey={colIndex}
|
|
87
|
+
index={index}
|
|
88
|
+
data={data}
|
|
89
|
+
columnNames={columnNames}
|
|
90
|
+
filterProps={options.setFilterChipProps ? options.setFilterChipProps(index, columnNames[index].name, data) : {}}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const getFilterList = filterList => {
|
|
95
|
+
return filterList.map((item, index) => {
|
|
96
|
+
if (columnNames[index].filterType === 'custom' && filterList[index].length) {
|
|
97
|
+
const filterListRenderersValue = filterListRenderers[index](item);
|
|
98
|
+
|
|
99
|
+
if (Array.isArray(filterListRenderersValue)) {
|
|
100
|
+
return filterListRenderersValue.map((customFilterItem, customFilterItemIndex) =>
|
|
101
|
+
customFilterChip(customFilterItem, index, customFilterItemIndex, item, true),
|
|
102
|
+
);
|
|
103
|
+
} else {
|
|
104
|
+
return customFilterChip(filterListRenderersValue, index, index, item, false);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return item.map((data, colIndex) => filterChip(index, data, colIndex));
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div className={classes.root}>
|
|
114
|
+
{serverSide && serverSideFilterList ? getFilterList(serverSideFilterList) : getFilterList(filterList)}
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
TableFilterList.propTypes = {
|
|
120
|
+
/** Data used to filter table against */
|
|
121
|
+
filterList: PropTypes.array.isRequired,
|
|
122
|
+
/** Filter List value renderers */
|
|
123
|
+
filterListRenderers: PropTypes.array.isRequired,
|
|
124
|
+
/** Columns used to describe table */
|
|
125
|
+
columnNames: PropTypes.arrayOf(
|
|
126
|
+
PropTypes.oneOfType([
|
|
127
|
+
PropTypes.string,
|
|
128
|
+
PropTypes.shape({ name: PropTypes.string.isRequired, filterType: PropTypes.string }),
|
|
129
|
+
]),
|
|
130
|
+
).isRequired,
|
|
131
|
+
/** Callback to trigger filter update */
|
|
132
|
+
onFilterUpdate: PropTypes.func,
|
|
133
|
+
ItemComponent: PropTypes.any,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export default TableFilterList;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Chip } from '@mui/material';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
|
|
6
|
+
const TableFilterListItem = ({ label, onDelete, className, filterProps }) => {
|
|
7
|
+
filterProps = filterProps || {};
|
|
8
|
+
if (filterProps.className) {
|
|
9
|
+
className = clsx(className, filterProps.className);
|
|
10
|
+
}
|
|
11
|
+
return <Chip label={label} onDelete={onDelete} className={className} {...filterProps} />;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
TableFilterListItem.propTypes = {
|
|
15
|
+
label: PropTypes.node,
|
|
16
|
+
onDelete: PropTypes.func.isRequired,
|
|
17
|
+
className: PropTypes.string.isRequired,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default TableFilterListItem;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import MuiTable from '@mui/material/Table';
|
|
3
|
+
import TablePagination from './TablePagination';
|
|
4
|
+
import { makeStyles } from 'tss-react/mui';
|
|
5
|
+
import PropTypes from 'prop-types';
|
|
6
|
+
|
|
7
|
+
const useStyles = makeStyles({ name: 'MUIDataTableFooter' })(() => ({
|
|
8
|
+
root: {
|
|
9
|
+
'@media print': {
|
|
10
|
+
display: 'none',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
const TableFooter = ({ options, rowCount, page, rowsPerPage, changeRowsPerPage, changePage }) => {
|
|
16
|
+
const { classes } = useStyles();
|
|
17
|
+
const { customFooter, pagination = true } = options;
|
|
18
|
+
|
|
19
|
+
if (customFooter) {
|
|
20
|
+
return (
|
|
21
|
+
<MuiTable className={classes.root}>
|
|
22
|
+
{options.customFooter(
|
|
23
|
+
rowCount,
|
|
24
|
+
page,
|
|
25
|
+
rowsPerPage,
|
|
26
|
+
changeRowsPerPage,
|
|
27
|
+
changePage,
|
|
28
|
+
options.textLabels.pagination,
|
|
29
|
+
)}
|
|
30
|
+
</MuiTable>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (pagination) {
|
|
35
|
+
return (
|
|
36
|
+
<MuiTable className={classes.root}>
|
|
37
|
+
<TablePagination
|
|
38
|
+
count={rowCount}
|
|
39
|
+
page={page}
|
|
40
|
+
rowsPerPage={rowsPerPage}
|
|
41
|
+
changeRowsPerPage={changeRowsPerPage}
|
|
42
|
+
changePage={changePage}
|
|
43
|
+
component={'div'}
|
|
44
|
+
options={options}
|
|
45
|
+
/>
|
|
46
|
+
</MuiTable>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return null;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
TableFooter.propTypes = {
|
|
54
|
+
/** Total number of table rows */
|
|
55
|
+
rowCount: PropTypes.number.isRequired,
|
|
56
|
+
/** Options used to describe table */
|
|
57
|
+
options: PropTypes.shape({
|
|
58
|
+
customFooter: PropTypes.func,
|
|
59
|
+
pagination: PropTypes.bool,
|
|
60
|
+
textLabels: PropTypes.shape({
|
|
61
|
+
pagination: PropTypes.object,
|
|
62
|
+
}),
|
|
63
|
+
}),
|
|
64
|
+
/** Current page index */
|
|
65
|
+
page: PropTypes.number.isRequired,
|
|
66
|
+
/** Total number allowed of rows per page */
|
|
67
|
+
rowsPerPage: PropTypes.number.isRequired,
|
|
68
|
+
/** Callback to trigger rows per page change */
|
|
69
|
+
changeRowsPerPage: PropTypes.func.isRequired,
|
|
70
|
+
/** Callback to trigger page change */
|
|
71
|
+
changePage: PropTypes.func.isRequired,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default TableFooter;
|