@arcblock/ux 2.1.7 → 2.1.10
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/lib/Datatable/CustomToolbar.js +38 -18
- package/lib/Datatable/DatatableContext.js +47 -0
- package/lib/Datatable/TableSearch.js +4 -6
- package/lib/Datatable/index.js +190 -37
- package/lib/Footer/index.js +2 -2
- package/lib/Header/header.js +6 -10
- package/lib/Layout/dashboard/header.js +1 -1
- package/lib/Layout/dashboard/index.js +1 -1
- package/lib/NavMenu/style.js +2 -2
- package/package.json +4 -4
- package/src/Datatable/CustomToolbar.js +108 -73
- package/src/Datatable/DatatableContext.js +32 -0
- package/src/Datatable/TableSearch.js +10 -7
- package/src/Datatable/index.js +200 -31
- package/src/Footer/index.js +2 -2
- package/src/Header/header.js +3 -21
- package/src/Layout/dashboard/header.js +1 -1
- package/src/Layout/dashboard/index.js +1 -1
- package/src/NavMenu/style.js +21 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcblock/ux",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.10",
|
|
4
4
|
"description": "Common used react components for arcblock products",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -52,10 +52,10 @@
|
|
|
52
52
|
"react": ">=18.1.0",
|
|
53
53
|
"react-ga": "^2.7.0"
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "f31615cda7024bde33cc8b4be093ab8e0baf46a1",
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@arcblock/icons": "^2.1.
|
|
58
|
-
"@arcblock/react-hooks": "^2.1.
|
|
57
|
+
"@arcblock/icons": "^2.1.10",
|
|
58
|
+
"@arcblock/react-hooks": "^2.1.10",
|
|
59
59
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
60
60
|
"@emotion/react": "^11.9.0",
|
|
61
61
|
"@emotion/styled": "^11.8.1",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useRef, isValidElement } from 'react';
|
|
1
|
+
import React, { useState, useRef, useEffect, isValidElement } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { TableFilter, TableViewCol } from 'mui-datatables';
|
|
4
4
|
import styled from 'styled-components';
|
|
@@ -17,8 +17,10 @@ import ListItemIcon from '@mui/material/ListItemIcon';
|
|
|
17
17
|
import ListItemText from '@mui/material/ListItemText';
|
|
18
18
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
|
19
19
|
import { useTheme } from '@mui/material/styles';
|
|
20
|
+
import LinearProgress from '@mui/material/LinearProgress';
|
|
20
21
|
import { handleCSVDownload } from './utils';
|
|
21
22
|
import TableSearch from './TableSearch';
|
|
23
|
+
import { useDatatableContext } from './DatatableContext';
|
|
22
24
|
|
|
23
25
|
function useMobile() {
|
|
24
26
|
const theme = useTheme();
|
|
@@ -30,6 +32,8 @@ export default function CustomToolbar(props) {
|
|
|
30
32
|
const moreBtn = useRef(null);
|
|
31
33
|
const isMobile = useMobile();
|
|
32
34
|
const toolbarId = useRef(Math.random().toString(32).slice(2));
|
|
35
|
+
const [searchOpened, setSearchOpened] = useState(false);
|
|
36
|
+
const { customButtons, loading, disabled } = useDatatableContext();
|
|
33
37
|
|
|
34
38
|
const {
|
|
35
39
|
data,
|
|
@@ -47,9 +51,10 @@ export default function CustomToolbar(props) {
|
|
|
47
51
|
searchText,
|
|
48
52
|
searchTextUpdate,
|
|
49
53
|
searchClose,
|
|
50
|
-
customButtons,
|
|
51
54
|
} = props;
|
|
52
55
|
|
|
56
|
+
const customToolbarEle = options.customToolbar ? options.customToolbar(props) : '';
|
|
57
|
+
|
|
53
58
|
const { search, downloadCsv, print, viewColumns, filterTable } = options.textLabels.toolbar;
|
|
54
59
|
|
|
55
60
|
const hideSearch = options.search === false || options.search === 'false';
|
|
@@ -58,6 +63,12 @@ export default function CustomToolbar(props) {
|
|
|
58
63
|
const TableFilterComponent = components.TableFilter || TableFilter;
|
|
59
64
|
const TableViewColComponent = components.TableViewCol || TableViewCol;
|
|
60
65
|
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (loading || disabled) {
|
|
68
|
+
setAllPopsEl({});
|
|
69
|
+
}
|
|
70
|
+
}, [loading, disabled]);
|
|
71
|
+
|
|
61
72
|
const printArea = func => {
|
|
62
73
|
return (
|
|
63
74
|
<ReactToPrint content={() => props.tableRef()}>
|
|
@@ -70,6 +81,7 @@ export default function CustomToolbar(props) {
|
|
|
70
81
|
|
|
71
82
|
const defaultButtons = [];
|
|
72
83
|
|
|
84
|
+
// download/viewColumns/filter button behaviours, rendered using custom button logic
|
|
73
85
|
if (!(options.download === false || options.download === 'false')) {
|
|
74
86
|
defaultButtons.push({
|
|
75
87
|
Icon: DownloadIcon,
|
|
@@ -127,6 +139,8 @@ export default function CustomToolbar(props) {
|
|
|
127
139
|
const allPops = [];
|
|
128
140
|
const [allPopsEl, setAllPopsEl] = useState({});
|
|
129
141
|
|
|
142
|
+
// Large screens show the toolbar buttons directly, small screens show the drop-down menu style buttons
|
|
143
|
+
// The right-hand button of the form toolbar in desktop mode
|
|
130
144
|
const toolbarButtons = [...defaultButtons, ...customButtons].map((e, index) => {
|
|
131
145
|
if (isValidElement(e)) {
|
|
132
146
|
return e;
|
|
@@ -136,6 +150,7 @@ export default function CustomToolbar(props) {
|
|
|
136
150
|
|
|
137
151
|
if (e.Icon) {
|
|
138
152
|
const { Icon, popRender } = e;
|
|
153
|
+
// When popRender is present, clicking the button will bubble up the content returned by the popRender
|
|
139
154
|
if (popRender) {
|
|
140
155
|
allPops.push(
|
|
141
156
|
<Popover
|
|
@@ -166,6 +181,7 @@ export default function CustomToolbar(props) {
|
|
|
166
181
|
}
|
|
167
182
|
|
|
168
183
|
if (popRender) {
|
|
184
|
+
// On the large screen, the bubble is positioned at the corresponding button
|
|
169
185
|
setAllPopsEl({
|
|
170
186
|
[popId]: document.getElementById(`btn-${popId}`),
|
|
171
187
|
});
|
|
@@ -180,6 +196,7 @@ export default function CustomToolbar(props) {
|
|
|
180
196
|
return e;
|
|
181
197
|
});
|
|
182
198
|
|
|
199
|
+
// The toolbar menu in the mobile to replace toolbarButtons
|
|
183
200
|
const menuItems = [...defaultButtons, ...customButtons].map((e, index) => {
|
|
184
201
|
const popId = getPopId(index);
|
|
185
202
|
|
|
@@ -210,6 +227,7 @@ export default function CustomToolbar(props) {
|
|
|
210
227
|
}
|
|
211
228
|
|
|
212
229
|
if (e.popRender) {
|
|
230
|
+
// On the small screen, the bubbles are positioned at the three dot buttons
|
|
213
231
|
setAllPopsEl({
|
|
214
232
|
[popId]: moreBtn.current,
|
|
215
233
|
});
|
|
@@ -223,54 +241,57 @@ export default function CustomToolbar(props) {
|
|
|
223
241
|
return (
|
|
224
242
|
<div>
|
|
225
243
|
<Container>
|
|
226
|
-
<div
|
|
244
|
+
<div
|
|
245
|
+
className={`custom-toobar-title ${
|
|
246
|
+
isMobile && searchOpened && isValidElement(title) ? 'toobar-title-hidden' : ''
|
|
247
|
+
}`}>
|
|
227
248
|
<div className="custom-toobar-title-inner">
|
|
228
249
|
<span>{title}</span>
|
|
229
250
|
</div>
|
|
230
251
|
</div>
|
|
231
|
-
<div className=
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
{
|
|
246
|
-
|
|
247
|
-
<
|
|
248
|
-
<
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
</div>
|
|
252
|
+
<div className={`custom-toobar-btns ${loading || disabled ? 'toobar-btns-disabled' : ''}`}>
|
|
253
|
+
{!hideSearch && (
|
|
254
|
+
<TableSearch
|
|
255
|
+
search={search}
|
|
256
|
+
options={options}
|
|
257
|
+
searchText={searchText}
|
|
258
|
+
searchTextUpdate={searchTextUpdate}
|
|
259
|
+
searchClose={searchClose}
|
|
260
|
+
onSearchOpen={setSearchOpened}
|
|
261
|
+
/>
|
|
262
|
+
)}
|
|
263
|
+
{!showMore && (
|
|
264
|
+
<>
|
|
265
|
+
{!hidePrint &&
|
|
266
|
+
printArea(({ handlePrint }) => (
|
|
267
|
+
<span>
|
|
268
|
+
<Tooltip title={print}>
|
|
269
|
+
<IconButton
|
|
270
|
+
data-testid={`${print}-iconButton`}
|
|
271
|
+
aria-label={print}
|
|
272
|
+
disabled={options.print === 'disabled'}
|
|
273
|
+
onClick={handlePrint}>
|
|
274
|
+
<PrintIcon />
|
|
275
|
+
</IconButton>
|
|
276
|
+
</Tooltip>
|
|
277
|
+
</span>
|
|
278
|
+
))}
|
|
279
|
+
|
|
280
|
+
{toolbarButtons}
|
|
281
|
+
</>
|
|
282
|
+
)}
|
|
283
|
+
{showMore && (
|
|
284
|
+
<IconButton
|
|
285
|
+
ref={moreBtn}
|
|
286
|
+
aria-haspopup="true"
|
|
287
|
+
aria-expanded={menuIconEl ? 'true' : undefined}
|
|
288
|
+
onClick={event => setMenuIconEl(event.currentTarget)}
|
|
289
|
+
style={{ flexShrink: 0 }}>
|
|
290
|
+
<MoreVertIcon />
|
|
291
|
+
</IconButton>
|
|
292
|
+
)}
|
|
273
293
|
</div>
|
|
294
|
+
{customToolbarEle}
|
|
274
295
|
</Container>
|
|
275
296
|
|
|
276
297
|
<Menu
|
|
@@ -298,6 +319,7 @@ export default function CustomToolbar(props) {
|
|
|
298
319
|
{allPops.map((e, index) => (
|
|
299
320
|
<div key={getPopId(index)}>{e}</div>
|
|
300
321
|
))}
|
|
322
|
+
{loading && <LinearProgress />}
|
|
301
323
|
</div>
|
|
302
324
|
);
|
|
303
325
|
}
|
|
@@ -314,12 +336,11 @@ CustomToolbar.propTypes = {
|
|
|
314
336
|
updateFilterByType: PropTypes.func.isRequired,
|
|
315
337
|
toggleViewColumn: PropTypes.func.isRequired,
|
|
316
338
|
updateColumns: PropTypes.func.isRequired,
|
|
317
|
-
title: PropTypes.
|
|
339
|
+
title: PropTypes.any,
|
|
318
340
|
searchText: PropTypes.any,
|
|
319
341
|
searchTextUpdate: PropTypes.func.isRequired,
|
|
320
342
|
searchClose: PropTypes.func.isRequired,
|
|
321
343
|
tableRef: PropTypes.func.isRequired,
|
|
322
|
-
customButtons: PropTypes.array.isRequired,
|
|
323
344
|
};
|
|
324
345
|
|
|
325
346
|
CustomToolbar.defaultProps = {
|
|
@@ -335,37 +356,51 @@ const Container = styled.div`
|
|
|
335
356
|
display: flex;
|
|
336
357
|
align-items: center;
|
|
337
358
|
height: 56px;
|
|
338
|
-
.custom-toobar {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
359
|
+
.custom-toobar-title {
|
|
360
|
+
position: relative;
|
|
361
|
+
flex: 1;
|
|
362
|
+
font-size: 18px;
|
|
363
|
+
font-weight: 800;
|
|
364
|
+
height: 56px;
|
|
365
|
+
transition: all ease 0.3s;
|
|
366
|
+
&-inner {
|
|
367
|
+
line-height: 56px;
|
|
368
|
+
width: 100%;
|
|
344
369
|
height: 56px;
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
370
|
+
position: absolute;
|
|
371
|
+
left: 0;
|
|
372
|
+
top: 0;
|
|
373
|
+
span {
|
|
374
|
+
display: inline-block;
|
|
375
|
+
max-width: 100%;
|
|
376
|
+
white-space: nowrap;
|
|
377
|
+
text-overflow: ellipsis;
|
|
378
|
+
overflow: hidden;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
.custom-toobar-btns {
|
|
383
|
+
display: flex;
|
|
384
|
+
justify-content: center;
|
|
385
|
+
align-items: center;
|
|
386
|
+
&.toobar-btns-disabled {
|
|
387
|
+
position: relative;
|
|
388
|
+
opacity: 0.5;
|
|
389
|
+
&:after {
|
|
349
390
|
position: absolute;
|
|
391
|
+
display: block;
|
|
392
|
+
z-index: 2;
|
|
393
|
+
width: 100%;
|
|
394
|
+
height: 100%;
|
|
350
395
|
left: 0;
|
|
351
396
|
top: 0;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
max-width: 100%;
|
|
355
|
-
white-space: nowrap;
|
|
356
|
-
text-overflow: ellipsis;
|
|
357
|
-
overflow: hidden;
|
|
358
|
-
}
|
|
397
|
+
content: '';
|
|
398
|
+
cursor: not-allowed;
|
|
359
399
|
}
|
|
360
400
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
&-btns {
|
|
366
|
-
display: flex;
|
|
367
|
-
justify-content: center;
|
|
368
|
-
align-items: center;
|
|
369
|
-
}
|
|
401
|
+
}
|
|
402
|
+
.toobar-title-hidden {
|
|
403
|
+
opacity: 0;
|
|
404
|
+
cursor: none;
|
|
370
405
|
}
|
|
371
406
|
`;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const DatatableContext = createContext({});
|
|
4
|
+
|
|
5
|
+
const { Provider } = DatatableContext;
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line react/prop-types
|
|
8
|
+
const DatatableProvide = ({ children }) => {
|
|
9
|
+
const [customButtons, setCustomButtons] = useState([]);
|
|
10
|
+
const [loading, setLoading] = useState(false);
|
|
11
|
+
const [disabled, setDisabled] = useState(false);
|
|
12
|
+
const [filterLabel, setFilterLabel] = useState('Filter');
|
|
13
|
+
|
|
14
|
+
const value = {
|
|
15
|
+
customButtons,
|
|
16
|
+
setCustomButtons,
|
|
17
|
+
filterLabel,
|
|
18
|
+
setFilterLabel,
|
|
19
|
+
loading,
|
|
20
|
+
setLoading,
|
|
21
|
+
disabled,
|
|
22
|
+
setDisabled,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return <Provider value={value}>{children}</Provider>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function useDatatableContext() {
|
|
29
|
+
return useContext(DatatableContext);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { DatatableProvide, useDatatableContext };
|
|
@@ -13,7 +13,6 @@ export default function TableSearch({
|
|
|
13
13
|
searchText,
|
|
14
14
|
searchTextUpdate,
|
|
15
15
|
searchClose,
|
|
16
|
-
isMobile,
|
|
17
16
|
onSearchOpen,
|
|
18
17
|
}) {
|
|
19
18
|
const [inputMode, setInputMode] = useState(false);
|
|
@@ -36,7 +35,7 @@ export default function TableSearch({
|
|
|
36
35
|
return (
|
|
37
36
|
<Container>
|
|
38
37
|
{inputMode ? (
|
|
39
|
-
<div className="toolbar-search-icon-placeholder
|
|
38
|
+
<div className="toolbar-search-icon-placeholder">
|
|
40
39
|
<SearchIcon />
|
|
41
40
|
</div>
|
|
42
41
|
) : (
|
|
@@ -51,10 +50,7 @@ export default function TableSearch({
|
|
|
51
50
|
</Tooltip>
|
|
52
51
|
)}
|
|
53
52
|
|
|
54
|
-
<div
|
|
55
|
-
className={`toolbar-search-area ${inputMode ? 'toolbar-btn-show' : ''} ${
|
|
56
|
-
isMobile ? 'small-textfield' : ''
|
|
57
|
-
}`}>
|
|
53
|
+
<div className={`toolbar-search-area ${inputMode ? 'toolbar-btn-show' : ''}`}>
|
|
58
54
|
{inputMode && (
|
|
59
55
|
<TextField
|
|
60
56
|
variant="standard"
|
|
@@ -81,7 +77,6 @@ TableSearch.propTypes = {
|
|
|
81
77
|
options: PropTypes.object.isRequired,
|
|
82
78
|
searchTextUpdate: PropTypes.func.isRequired,
|
|
83
79
|
searchClose: PropTypes.func.isRequired,
|
|
84
|
-
isMobile: PropTypes.bool.isRequired,
|
|
85
80
|
};
|
|
86
81
|
|
|
87
82
|
TableSearch.defaultProps = {
|
|
@@ -104,6 +99,14 @@ const Container = styled.div`
|
|
|
104
99
|
&.toolbar-btn-show {
|
|
105
100
|
width: 260px;
|
|
106
101
|
padding-left: 8px;
|
|
102
|
+
|
|
103
|
+
${props => props.theme.breakpoints.down('md')} {
|
|
104
|
+
width: 200px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
${props => props.theme.breakpoints.down('sm')} {
|
|
108
|
+
width: 180px;
|
|
109
|
+
}
|
|
107
110
|
&.small-textfield {
|
|
108
111
|
width: 200px;
|
|
109
112
|
}
|
package/src/Datatable/index.js
CHANGED
|
@@ -1,34 +1,105 @@
|
|
|
1
|
-
import React, { useRef } from 'react';
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import MUIDataTable, { TableFilterList } from 'mui-datatables';
|
|
3
|
+
import MUIDataTable, { TableFilterList, TableFooter } from 'mui-datatables';
|
|
4
4
|
import styled from 'styled-components';
|
|
5
|
+
import isObject from 'lodash/isObject';
|
|
6
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
5
7
|
import CustomToolbar from './CustomToolbar';
|
|
8
|
+
import { DatatableProvide, useDatatableContext } from './DatatableContext';
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<TableFilterList {...props} />
|
|
15
|
-
</div>
|
|
16
|
-
</FilterLine>
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
return '';
|
|
20
|
-
};
|
|
10
|
+
export default function Datatable({ ...props }) {
|
|
11
|
+
return (
|
|
12
|
+
<DatatableProvide>
|
|
13
|
+
<ReDatatable {...props} />
|
|
14
|
+
</DatatableProvide>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
18
|
+
/**
|
|
19
|
+
* @param {Object} props.options The options of mui-datatable,detail see https://github.com/gregnb/mui-datatables/tree/b8d2eee6af4589d254b40918e5d7e70b1ee4baca
|
|
20
|
+
* @param {Array} props.customButtons Custom buttons for toolbar
|
|
21
|
+
* @param {Function} props.onChange When onChange is present, serverSide mode is activated by default https://github.com/gregnb/mui-datatables/tree/b8d2eee6af4589d254b40918e5d7e70b1ee4baca#remote-data
|
|
22
|
+
* @param {Boolean} props.loading For dynamic data, usually used with onChange
|
|
23
|
+
* @returns
|
|
24
|
+
*/
|
|
25
|
+
function ReDatatable({
|
|
26
|
+
data: originData,
|
|
27
|
+
columns: originColumns,
|
|
28
|
+
locale,
|
|
29
|
+
options,
|
|
30
|
+
style,
|
|
31
|
+
customButtons,
|
|
32
|
+
onChange,
|
|
33
|
+
loading,
|
|
34
|
+
disabled,
|
|
35
|
+
...rest
|
|
36
|
+
}) {
|
|
37
|
+
const container = useRef(null);
|
|
38
|
+
const oldState = useRef(null);
|
|
39
|
+
const { setCustomButtons, setFilterLabel, setLoading, setDisabled } = useDatatableContext();
|
|
25
40
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
const disabledCellStyle = {
|
|
42
|
+
cursor: 'not-allowed',
|
|
43
|
+
pointerEvents: 'none',
|
|
44
|
+
};
|
|
29
45
|
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
const keys = [];
|
|
47
|
+
|
|
48
|
+
// Convert Columns fields to object sets to support the width function
|
|
49
|
+
const columns = originColumns.map(e => {
|
|
50
|
+
let tempObj;
|
|
51
|
+
|
|
52
|
+
if (!isObject(e)) {
|
|
53
|
+
tempObj = {
|
|
54
|
+
label: e,
|
|
55
|
+
name: e,
|
|
56
|
+
};
|
|
57
|
+
} else {
|
|
58
|
+
tempObj = cloneDeep(e);
|
|
59
|
+
}
|
|
60
|
+
keys.push(tempObj.name);
|
|
61
|
+
|
|
62
|
+
if (!tempObj.options) {
|
|
63
|
+
tempObj.options = {};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { setCellHeaderProps } = tempObj.options;
|
|
67
|
+
tempObj.options.setCellHeaderProps = columnMeta => {
|
|
68
|
+
let cellProps = {};
|
|
69
|
+
|
|
70
|
+
// Complementing width while inheriting old setCellHeaderProps
|
|
71
|
+
if (setCellHeaderProps && !setCellHeaderProps.__innerFunc) {
|
|
72
|
+
cellProps = setCellHeaderProps(columnMeta) || {};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (loading || disabled) {
|
|
76
|
+
cellProps = { ...cellProps, style: disabledCellStyle };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (tempObj.width) {
|
|
80
|
+
cellProps.width = tempObj.width;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return cellProps;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Prevent memory xie caused by recursive forwarding of setCellHeaderProps functions
|
|
87
|
+
tempObj.options.setCellHeaderProps.__innerFunc = 1;
|
|
88
|
+
|
|
89
|
+
return tempObj;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Fixing object-type structures
|
|
93
|
+
const data = originData.map(e => {
|
|
94
|
+
if (!Array.isArray(e) && isObject(e)) {
|
|
95
|
+
return keys.map(key => e[key]);
|
|
96
|
+
}
|
|
97
|
+
return e;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
useEffect(() => setCustomButtons(customButtons || []), [customButtons]);
|
|
101
|
+
useEffect(() => setLoading(loading), [loading]);
|
|
102
|
+
useEffect(() => setDisabled(disabled), [disabled]);
|
|
32
103
|
|
|
33
104
|
let textLabels = {
|
|
34
105
|
body: { noMatch: 'Sorry, no matching records found', toolTip: 'Sort' },
|
|
@@ -74,42 +145,78 @@ export default function Datatable({ locale, options, style, customButtons, ...re
|
|
|
74
145
|
};
|
|
75
146
|
}
|
|
76
147
|
|
|
148
|
+
useEffect(() => setFilterLabel(textLabels.filter.title), [textLabels.filter.title]);
|
|
149
|
+
|
|
77
150
|
const opts = {
|
|
78
151
|
selectableRows: 'none',
|
|
79
152
|
textLabels,
|
|
153
|
+
rowsPerPage: 10,
|
|
154
|
+
rowsPerPageOptions: [10, 20, 50],
|
|
80
155
|
...options,
|
|
81
156
|
};
|
|
82
157
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
158
|
+
if (onChange) {
|
|
159
|
+
Object.assign(opts, {
|
|
160
|
+
serverSide: true,
|
|
161
|
+
// Wrap the more friendly onChange callback by listening to onTableChange,
|
|
162
|
+
// which will only be triggered when the table key state changes
|
|
163
|
+
onTableChange: (action, tableState) => {
|
|
164
|
+
if (action === 'propsUpdate') {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const state = {
|
|
168
|
+
count: tableState.count,
|
|
169
|
+
page: tableState.page,
|
|
170
|
+
rowsPerPage: tableState.rowsPerPage,
|
|
171
|
+
searchText: tableState.searchText,
|
|
172
|
+
sortOrder: tableState.sortOrder, //
|
|
173
|
+
filterList: tableState.filterList,
|
|
174
|
+
};
|
|
175
|
+
const stateStr = JSON.stringify(state);
|
|
176
|
+
if (stateStr === oldState.current) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
oldState.current = stateStr;
|
|
180
|
+
onChange(state, action);
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
86
184
|
|
|
87
185
|
const props = {
|
|
88
186
|
options: opts,
|
|
89
187
|
...rest,
|
|
90
188
|
components: {
|
|
91
|
-
TableToolbar:
|
|
189
|
+
TableToolbar: CustomToolbar,
|
|
190
|
+
TableFooter: WrapTableFooter,
|
|
92
191
|
TableFilterList: WrapFilterList,
|
|
93
192
|
},
|
|
94
193
|
};
|
|
95
194
|
|
|
96
|
-
|
|
195
|
+
ReDatatable.propTypes = {
|
|
196
|
+
data: PropTypes.array.isRequired,
|
|
197
|
+
columns: PropTypes.array.isRequired,
|
|
97
198
|
options: PropTypes.object,
|
|
98
199
|
style: PropTypes.object,
|
|
99
200
|
locale: PropTypes.string,
|
|
201
|
+
loading: PropTypes.bool,
|
|
202
|
+
disabled: PropTypes.bool,
|
|
100
203
|
customButtons: PropTypes.array,
|
|
204
|
+
onChange: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
|
|
101
205
|
};
|
|
102
206
|
|
|
103
|
-
|
|
207
|
+
ReDatatable.defaultProps = {
|
|
104
208
|
options: {},
|
|
105
209
|
style: {},
|
|
106
210
|
locale: 'en',
|
|
211
|
+
loading: false,
|
|
212
|
+
disabled: false,
|
|
107
213
|
customButtons: [],
|
|
214
|
+
onChange: '',
|
|
108
215
|
};
|
|
109
216
|
|
|
110
217
|
return (
|
|
111
218
|
<TableContainer ref={container} style={style}>
|
|
112
|
-
<MUIDataTable {...props} />
|
|
219
|
+
<MUIDataTable data={data} columns={columns} {...props} />
|
|
113
220
|
</TableContainer>
|
|
114
221
|
);
|
|
115
222
|
}
|
|
@@ -143,3 +250,65 @@ const FilterLine = styled.div`
|
|
|
143
250
|
font-size: 14px;
|
|
144
251
|
}
|
|
145
252
|
`;
|
|
253
|
+
|
|
254
|
+
const WrapFilterList = props => {
|
|
255
|
+
const { filterLabel } = useDatatableContext();
|
|
256
|
+
const hasFilter = !!props.filterList.filter(e => e.length).length;
|
|
257
|
+
if (hasFilter) {
|
|
258
|
+
return (
|
|
259
|
+
<FilterLine>
|
|
260
|
+
{hasFilter && <div className="toolbar-filter-title">{filterLabel}</div>}
|
|
261
|
+
<div className="toolbar-filter-content">
|
|
262
|
+
<TableFilterList {...props} />
|
|
263
|
+
</div>
|
|
264
|
+
</FilterLine>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return '';
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
WrapFilterList.propTypes = {
|
|
271
|
+
filterList: PropTypes.array,
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
WrapFilterList.defaultProps = {
|
|
275
|
+
filterList: [],
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const WrapTableFooter = props => {
|
|
279
|
+
const { loading, disabled } = useDatatableContext();
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
<FooterContainer>
|
|
283
|
+
<div className={`datatable-footer ${loading || disabled ? 'datatable-footer-disabled' : ''}`}>
|
|
284
|
+
<TableFooter {...props} />
|
|
285
|
+
</div>
|
|
286
|
+
</FooterContainer>
|
|
287
|
+
);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const FooterContainer = styled.div`
|
|
291
|
+
display: flex;
|
|
292
|
+
align-items: center;
|
|
293
|
+
.datatable-footer {
|
|
294
|
+
position: relative;
|
|
295
|
+
margin-left: auto;
|
|
296
|
+
&.datatable-footer-disabled {
|
|
297
|
+
position: relative;
|
|
298
|
+
.MuiTablePagination-root {
|
|
299
|
+
opacity: 0.6;
|
|
300
|
+
}
|
|
301
|
+
&:after {
|
|
302
|
+
position: absolute;
|
|
303
|
+
display: block;
|
|
304
|
+
z-index: 2;
|
|
305
|
+
width: 100%;
|
|
306
|
+
height: 100%;
|
|
307
|
+
left: 0;
|
|
308
|
+
top: 0;
|
|
309
|
+
content: '';
|
|
310
|
+
cursor: not-allowed;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
`;
|