material-react-table 0.5.5 → 0.5.8

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,5 +1,16 @@
1
1
  import { FC } from 'react';
2
2
  import { MRT_HeaderGroup } from '..';
3
+ export declare const commonMenuItemStyles: {
4
+ py: string;
5
+ my: number;
6
+ justifyContent: string;
7
+ alignItems: string;
8
+ };
9
+ export declare const commonListItemStyles: {
10
+ display: string;
11
+ gap: string;
12
+ alignItems: string;
13
+ };
3
14
  interface Props {
4
15
  anchorEl: HTMLElement | null;
5
16
  column: MRT_HeaderGroup;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.5.5",
2
+ "version": "0.5.8",
3
3
  "license": "MIT",
4
4
  "name": "material-react-table",
5
5
  "description": "A fully featured Material-UI implementation of react-table, inspired by material-table and the mui DataGrid, written from the ground up in TypeScript.",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "scripts": {
30
30
  "analyze": "size-limit --why",
31
- "build": "tsdx build && size-limit",
31
+ "build": "tsdx build && size-limit && rm -rf material-react-table-docs/node_modules/material-react-table/dist && cp -r dist material-react-table-docs/node_modules/material-react-table/ && cp -r src material-react-table-docs/node_modules/material-react-table/ && cp -r package.json material-react-table-docs/node_modules/material-react-table/package.json",
32
32
  "build-storybook": "build-storybook",
33
33
  "format": "prettier -w .",
34
34
  "lint": "tsdx lint",
@@ -36,8 +36,7 @@
36
36
  "size": "size-limit",
37
37
  "start": "tsdx watch",
38
38
  "storybook": "start-storybook -p 6006",
39
- "test": "tsdx test --passWithNoTests",
40
- "upgrade": "ncu -u && npm i"
39
+ "test": "tsdx test --passWithNoTests"
41
40
  },
42
41
  "husky": {
43
42
  "hooks": {
@@ -60,9 +59,9 @@
60
59
  "@emotion/react": "^11.8.1",
61
60
  "@emotion/styled": "^11.8.1",
62
61
  "@etchteam/storybook-addon-status": "^4.2.0",
63
- "@faker-js/faker": "^6.0.0-alpha.7",
64
- "@mui/icons-material": "^5.4.4",
65
- "@mui/material": "^5.4.4",
62
+ "@faker-js/faker": "^6.0.0-beta.0",
63
+ "@mui/icons-material": "^5.5.0",
64
+ "@mui/material": "^5.5.0",
66
65
  "@size-limit/preset-small-lib": "^7.0.8",
67
66
  "@storybook/addon-a11y": "^6.4.19",
68
67
  "@storybook/addon-actions": "^6.4.19",
@@ -2,6 +2,8 @@ import React, { ChangeEvent, FC, MouseEvent, ReactNode } from 'react';
2
2
  import {
3
3
  AlertProps,
4
4
  IconButtonProps,
5
+ LinearProgressProps,
6
+ SkeletonProps,
5
7
  TableBodyProps,
6
8
  TableCellProps,
7
9
  TableContainerProps,
@@ -17,6 +19,7 @@ import {
17
19
  Cell,
18
20
  Column,
19
21
  ColumnInstance,
22
+ FilterType,
20
23
  // ColumnInterface,
21
24
  HeaderGroup,
22
25
  Row,
@@ -130,6 +133,8 @@ export type MRT_ColumnInterface<D extends {} = {}> =
130
133
  Header?: string;
131
134
  disableFilters?: boolean;
132
135
  editable?: boolean;
136
+ filter?: MRT_FilterType | string | FilterType<D>;
137
+ filterSelectOptions?: (string | { text: string; value: string })[];
133
138
  muiTableBodyCellEditTextFieldProps?:
134
139
  | TextFieldProps
135
140
  | ((cell: MRT_Cell<D>) => TextFieldProps);
@@ -181,15 +186,18 @@ export type MRT_Cell<D extends {} = {}, _V = any> = Cell<D> &
181
186
  UseGroupByCellProps<D> &
182
187
  UseRowStateCellProps<D> & {};
183
188
 
184
- export type MRT_FilterType =
185
- | 'contains'
186
- | 'empty'
187
- | 'endsWith'
188
- | 'equals'
189
- | 'fuzzy'
190
- | 'notEmpty'
191
- | 'notEquals'
192
- | 'startsWith';
189
+ export enum MRT_FILTER_TYPE {
190
+ CONTAINS = 'contains',
191
+ EMPTY = 'empty',
192
+ ENDS_WITH = 'endsWith',
193
+ EQUALS = 'equals',
194
+ FUZZY = 'fuzzy',
195
+ NOT_EMPTY = 'notEmpty',
196
+ NOT_EQUALS = 'notEquals',
197
+ STARTS_WITH = 'startsWith',
198
+ }
199
+
200
+ export type MRT_FilterType = MRT_FILTER_TYPE | Function;
193
201
 
194
202
  export type MRT_TableState<D extends {} = {}> = TableState<D> &
195
203
  UseColumnOrderState<D> &
@@ -244,6 +252,9 @@ export type MaterialReactTableProps<D extends {} = {}> = UseTableOptions<D> &
244
252
  isFetching?: boolean;
245
253
  isLoading?: boolean;
246
254
  localization?: Partial<MRT_Localization>;
255
+ muiLinearProgressProps?:
256
+ | LinearProgressProps
257
+ | ((tableInstance: MRT_TableInstance) => LinearProgressProps);
247
258
  muiSearchTextFieldProps?: TextFieldProps;
248
259
  muiTableBodyCellEditTextFieldProps?:
249
260
  | TextFieldProps
@@ -251,6 +262,9 @@ export type MaterialReactTableProps<D extends {} = {}> = UseTableOptions<D> &
251
262
  muiTableBodyCellProps?:
252
263
  | TableCellProps
253
264
  | ((cell?: MRT_Cell<D>) => TableCellProps);
265
+ muiTableBodyCellSkeletonProps?:
266
+ | SkeletonProps
267
+ | ((cell?: MRT_Cell<D>) => SkeletonProps);
254
268
  muiTableBodyProps?:
255
269
  | TableBodyProps
256
270
  | ((tableInstance: MRT_TableInstance<D>) => TableBodyProps);
@@ -1,5 +1,5 @@
1
1
  import React, { FC, MouseEvent } from 'react';
2
- import { TableCell, TableCellProps } from '@mui/material';
2
+ import { Skeleton, TableCell, TableCellProps } from '@mui/material';
3
3
  import { useMRT } from '../useMRT';
4
4
  import { MRT_EditCellTextField } from '../inputs/MRT_EditCellTextField';
5
5
  import { MRT_Cell } from '..';
@@ -22,8 +22,10 @@ interface Props {
22
22
 
23
23
  export const MRT_TableBodyCell: FC<Props> = ({ cell }) => {
24
24
  const {
25
- onCellClick,
25
+ isLoading,
26
26
  muiTableBodyCellProps,
27
+ muiTableBodyCellSkeletonProps,
28
+ onCellClick,
27
29
  tableInstance: {
28
30
  state: { currentEditingRow, densePadding },
29
31
  },
@@ -63,7 +65,14 @@ export const MRT_TableBodyCell: FC<Props> = ({ cell }) => {
63
65
  } as TableCellProps['sx']
64
66
  }
65
67
  >
66
- {currentEditingRow?.id === cell.row.id ? (
68
+ {isLoading ? (
69
+ <Skeleton
70
+ animation="wave"
71
+ height={20}
72
+ width={Math.random() * (120 - 60) + 60}
73
+ {...muiTableBodyCellSkeletonProps}
74
+ />
75
+ ) : currentEditingRow?.id === cell.row.id ? (
67
76
  <MRT_EditCellTextField cell={cell} />
68
77
  ) : cell.isPlaceholder ? null : cell.isAggregated ? (
69
78
  cell.render('Aggregated')
@@ -7,7 +7,6 @@ import {
7
7
  Divider,
8
8
  IconButtonProps,
9
9
  Box,
10
- MenuList,
11
10
  } from '@mui/material';
12
11
  import { useMRT } from '../useMRT';
13
12
  import { MRT_ShowHideColumnsMenu } from '../menus/MRT_ShowHideColumnsMenu';
@@ -44,39 +43,40 @@ export const MRT_ShowHideColumnsButton: FC<Props> = ({ ...rest }) => {
44
43
  anchorEl={anchorEl}
45
44
  open={!!anchorEl}
46
45
  onClose={() => setAnchorEl(null)}
46
+ MenuListProps={{
47
+ dense: tableInstance.state.densePadding,
48
+ }}
47
49
  >
48
- <MenuList dense={tableInstance.state.densePadding} disablePadding>
49
- <Box
50
- sx={{
51
- display: 'flex',
52
- justifyContent: 'space-between',
53
- p: '0 0.5rem 0.5rem 0.5rem',
54
- }}
50
+ <Box
51
+ sx={{
52
+ display: 'flex',
53
+ justifyContent: 'space-between',
54
+ p: '0.5rem',
55
+ }}
56
+ >
57
+ <Button
58
+ disabled={
59
+ !tableInstance.getToggleHideAllColumnsProps().checked &&
60
+ !tableInstance.getToggleHideAllColumnsProps().indeterminate
61
+ }
62
+ onClick={() => tableInstance.toggleHideAllColumns(true)}
63
+ >
64
+ {localization.columnShowHideMenuHideAll}
65
+ </Button>
66
+ <Button
67
+ disabled={tableInstance.getToggleHideAllColumnsProps().checked}
68
+ onClick={() => tableInstance.toggleHideAllColumns(false)}
55
69
  >
56
- <Button
57
- disabled={
58
- !tableInstance.getToggleHideAllColumnsProps().checked &&
59
- !tableInstance.getToggleHideAllColumnsProps().indeterminate
60
- }
61
- onClick={() => tableInstance.toggleHideAllColumns(true)}
62
- >
63
- {localization.columnShowHideMenuHideAll}
64
- </Button>
65
- <Button
66
- disabled={tableInstance.getToggleHideAllColumnsProps().checked}
67
- onClick={() => tableInstance.toggleHideAllColumns(false)}
68
- >
69
- {localization.columnShowHideMenuShowAll}
70
- </Button>
71
- </Box>
72
- <Divider />
73
- {tableInstance.columns.map((column: MRT_ColumnInstance, index) => (
74
- <MRT_ShowHideColumnsMenu
75
- key={`${index}-${column.id}`}
76
- column={column}
77
- />
78
- ))}
79
- </MenuList>
70
+ {localization.columnShowHideMenuShowAll}
71
+ </Button>
72
+ </Box>
73
+ <Divider />
74
+ {tableInstance.columns.map((column: MRT_ColumnInstance, index) => (
75
+ <MRT_ShowHideColumnsMenu
76
+ key={`${index}-${column.id}`}
77
+ column={column}
78
+ />
79
+ ))}
80
80
  </Menu>
81
81
  </>
82
82
  );
package/src/filtersFNs.ts CHANGED
@@ -1,6 +1,18 @@
1
1
  import { matchSorter } from 'match-sorter';
2
2
  import { MRT_Row } from '.';
3
3
 
4
+ export const fuzzySearchFN = (
5
+ rows: MRT_Row[],
6
+ columnIds: string[],
7
+ filterValue: string | number,
8
+ ) =>
9
+ matchSorter(rows, filterValue.toString().trim(), {
10
+ keys: columnIds.map((c) => `values.${c}`),
11
+ sorter: (rankedItems) => rankedItems,
12
+ });
13
+
14
+ fuzzySearchFN.autoRemove = (val: any) => !val;
15
+
4
16
  export const fuzzyFilterFN = (
5
17
  rows: MRT_Row[],
6
18
  id: string,
@@ -79,18 +79,7 @@ export const MRT_TableHeadCell: FC<Props> = ({ column }) => {
79
79
  const filterTooltip = !!column.filterValue
80
80
  ? localization.filterApplied
81
81
  .replace('{column}', String(column.Header))
82
- .replace(
83
- '{filterType}',
84
- // @ts-ignore
85
- localization[
86
- `filterMenuItem${
87
- tableInstance.state.currentFilterTypes[column.id]
88
- .charAt(0)
89
- .toUpperCase() +
90
- tableInstance.state.currentFilterTypes[column.id].slice(1)
91
- }`
92
- ],
93
- )
82
+ .replace('{filterType}', column.filterValue)
94
83
  : localization.toggleFilterButtonTitle;
95
84
 
96
85
  const columnHeader = column.render('Header') as string;
@@ -127,7 +116,7 @@ export const MRT_TableHeadCell: FC<Props> = ({ column }) => {
127
116
  >
128
117
  {column.render('Header')}
129
118
  {!isParentHeader && column.canSort && (
130
- <Tooltip arrow title={sortTooltip}>
119
+ <Tooltip arrow placement="top" title={sortTooltip}>
131
120
  <TableSortLabel
132
121
  aria-label={sortTooltip}
133
122
  active={column.isSorted}
@@ -136,19 +125,21 @@ export const MRT_TableHeadCell: FC<Props> = ({ column }) => {
136
125
  </Tooltip>
137
126
  )}
138
127
  {!isParentHeader && !!column.canFilter && (
139
- <Tooltip arrow title={filterTooltip}>
128
+ <Tooltip arrow placement="top" title={filterTooltip}>
140
129
  <IconButton
130
+ disableRipple
141
131
  onClick={(event) => {
142
132
  event.stopPropagation();
143
133
  setShowFilters(!tableInstance.state.showFilters);
144
134
  }}
145
135
  size="small"
146
136
  sx={{
137
+ m: 0,
147
138
  opacity: !!column.filterValue ? 0.8 : 0,
148
139
  p: '2px',
149
- m: 0,
150
140
  transition: 'all 0.2s ease-in-out',
151
141
  '&:hover': {
142
+ backgroundColor: 'transparent',
152
143
  opacity: 0.8,
153
144
  },
154
145
  }}
@@ -3,13 +3,14 @@ import {
3
3
  Chip,
4
4
  IconButton,
5
5
  InputAdornment,
6
+ MenuItem,
6
7
  TextField,
7
8
  TextFieldProps,
8
9
  Tooltip,
9
10
  } from '@mui/material';
10
11
  import { useAsyncDebounce } from 'react-table';
11
12
  import { useMRT } from '../useMRT';
12
- import { MRT_HeaderGroup } from '..';
13
+ import { MRT_FILTER_TYPE, MRT_HeaderGroup } from '..';
13
14
  import { MRT_FilterTypeMenu } from '../menus/MRT_FilterTypeMenu';
14
15
 
15
16
  interface Props {
@@ -65,7 +66,10 @@ export const MRT_FilterTextField: FC<Props> = ({ column }) => {
65
66
  const handleClearFilterChip = () => {
66
67
  setFilterValue('');
67
68
  column.setFilter(undefined);
68
- setCurrentFilterTypes((prev) => ({ ...prev, [column.id]: 'fuzzy' }));
69
+ setCurrentFilterTypes((prev) => ({
70
+ ...prev,
71
+ [column.id]: MRT_FILTER_TYPE.FUZZY,
72
+ }));
69
73
  };
70
74
 
71
75
  if (column.Filter) {
@@ -73,7 +77,13 @@ export const MRT_FilterTextField: FC<Props> = ({ column }) => {
73
77
  }
74
78
 
75
79
  const filterType = tableInstance.state.currentFilterTypes[column.id];
76
- const filterChipLabel = ['empty', 'notEmpty'].includes(filterType);
80
+ const isCustomFilterType = filterType instanceof Function;
81
+ const isSelectFilter = !!column.filterSelectOptions;
82
+ const filterChipLabel =
83
+ !isCustomFilterType &&
84
+ [MRT_FILTER_TYPE.EMPTY, MRT_FILTER_TYPE.NOT_EMPTY].includes(
85
+ filterType as MRT_FILTER_TYPE,
86
+ );
77
87
  const filterPlaceholder = localization.filterTextFieldPlaceholder?.replace(
78
88
  '{column}',
79
89
  String(column.Header),
@@ -88,30 +98,44 @@ export const MRT_FilterTextField: FC<Props> = ({ column }) => {
88
98
  disabled: !!filterChipLabel,
89
99
  sx: {
90
100
  textOverflow: 'ellipsis',
91
- width: filterChipLabel ? 0 : 'auto',
101
+ width: filterChipLabel ? 0 : undefined,
102
+ },
103
+ title: filterPlaceholder,
104
+ }}
105
+ label={isSelectFilter && !filterValue ? filterPlaceholder : undefined}
106
+ InputLabelProps={{
107
+ shrink: false,
108
+ sx: {
109
+ maxWidth: 'calc(100% - 2.5rem)',
92
110
  },
93
111
  title: filterPlaceholder,
94
112
  }}
95
113
  margin="none"
96
- placeholder={filterChipLabel ? '' : filterPlaceholder}
114
+ placeholder={
115
+ filterChipLabel || isSelectFilter ? undefined : filterPlaceholder
116
+ }
97
117
  onChange={(e: ChangeEvent<HTMLInputElement>) => {
98
118
  setFilterValue(e.target.value);
99
119
  handleChange(e.target.value);
100
120
  }}
101
121
  onClick={(e) => e.stopPropagation()}
122
+ select={isSelectFilter}
102
123
  value={filterValue ?? ''}
103
124
  variant="standard"
104
125
  InputProps={{
105
- startAdornment: (
126
+ startAdornment: !isSelectFilter && (
106
127
  <InputAdornment position="start">
107
- <Tooltip arrow title="Change Filter Mode">
108
- <IconButton
109
- onClick={handleFilterMenuOpen}
110
- size="small"
111
- sx={{ height: '1.75rem', width: '1.75rem' }}
112
- >
113
- <FilterListIcon />
114
- </IconButton>
128
+ <Tooltip arrow title={localization.changeFilterMode}>
129
+ <span>
130
+ <IconButton
131
+ disabled={isCustomFilterType}
132
+ onClick={handleFilterMenuOpen}
133
+ size="small"
134
+ sx={{ height: '1.75rem', width: '1.75rem' }}
135
+ >
136
+ <FilterListIcon />
137
+ </IconButton>
138
+ </span>
115
139
  </Tooltip>
116
140
  {filterChipLabel && (
117
141
  <Chip onDelete={handleClearFilterChip} label={filterType} />
@@ -122,16 +146,20 @@ export const MRT_FilterTextField: FC<Props> = ({ column }) => {
122
146
  <InputAdornment position="end">
123
147
  <Tooltip
124
148
  arrow
149
+ disableHoverListener={isSelectFilter}
125
150
  placement="right"
126
151
  title={localization.filterTextFieldClearButtonTitle ?? ''}
127
152
  >
128
153
  <span>
129
154
  <IconButton
130
155
  aria-label={localization.filterTextFieldClearButtonTitle}
131
- disabled={filterValue?.length === 0}
156
+ disabled={!filterValue?.length}
132
157
  onClick={handleClear}
133
158
  size="small"
134
- sx={{ height: '1.75rem', width: '1.75rem' }}
159
+ sx={{
160
+ height: '1.75rem',
161
+ width: '1.75rem',
162
+ }}
135
163
  >
136
164
  <CloseIcon fontSize="small" />
137
165
  </IconButton>
@@ -145,9 +173,35 @@ export const MRT_FilterTextField: FC<Props> = ({ column }) => {
145
173
  m: '0 -0.25rem',
146
174
  minWidth: !filterChipLabel ? '5rem' : 'auto',
147
175
  width: 'calc(100% + 0.5rem)',
176
+ mt: isSelectFilter && !filterValue ? '-1rem' : undefined,
177
+ '& .MuiSelect-icon': {
178
+ mr: '1.5rem',
179
+ },
148
180
  ...textFieldProps?.sx,
149
181
  }}
150
- />
182
+ >
183
+ {isSelectFilter && (
184
+ <MenuItem divider disabled={!filterValue} value="">
185
+ {localization.filterTextFieldClearButtonTitle}
186
+ </MenuItem>
187
+ )}
188
+ {column?.filterSelectOptions?.map((option) => {
189
+ let value;
190
+ let text;
191
+ if (typeof option === 'string') {
192
+ value = option;
193
+ text = option;
194
+ } else if (typeof option === 'object') {
195
+ value = option.value;
196
+ text = option.text;
197
+ }
198
+ return (
199
+ <MenuItem key={value} value={value}>
200
+ {text}
201
+ </MenuItem>
202
+ );
203
+ })}
204
+ </TextField>
151
205
  <MRT_FilterTypeMenu
152
206
  anchorEl={anchorEl}
153
207
  column={column}
@@ -26,6 +26,7 @@ export interface MRT_Localization {
26
26
  filterTextFieldChipLabelNotEmpty: string;
27
27
  filterTextFieldClearButtonTitle: string;
28
28
  filterTextFieldPlaceholder: string;
29
+ changeFilterMode: string;
29
30
  rowActionButtonCancel: string;
30
31
  rowActionButtonSave: string;
31
32
  rowActionMenuButtonTitle: string;
@@ -63,6 +64,7 @@ export const MRT_DefaultLocalization_EN: MRT_Localization = {
63
64
  filterMenuItemEmpty: 'Empty',
64
65
  filterMenuItemEndsWith: 'Ends With',
65
66
  filterMenuItemEquals: 'Equals',
67
+ changeFilterMode: 'Change filter mode',
66
68
  filterMenuItemFuzzy: 'Fuzzy Match (Default)',
67
69
  filterMenuItemNotEmpty: 'Not Empty',
68
70
  filterMenuItemNotEquals: 'Not Equals',